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

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

/* USER CODE BEGIN Includes */
#include "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_reed_hall_ext_int;
extern volatile uint8_t sts_work_mode, sts_cloud_netcolor, sts_lamp_bar_color, sts_status_color;

volatile uint8_t sts_fall_detection_acc_threshold = 10, sts_fall_detection_depth_threshold=30, sts_occupancy_overtime_threshold=20;
extern volatile uint32_t event_start_time, event_stop_time;
volatile uint8_t sts_occupancy_overtime_state = 0;
volatile uint8_t sts_presence_fall_detection=1;
volatile uint32_t SamplingPeriodicity = 1000; 		//unit ms
volatile uint32_t HeartBeatPeriodicity = 120000; 		//unit ms
volatile uint8_t STS_LoRa_WAN_Joined = 0;
volatile uint8_t mems_int1_detected = 0;

volatile uint8_t heart_beat_timer =0;
char outbuf[128]="";
volatile sts_cfg_nvm_t sts_cfg_nvm = {
		sts_mtmcode1,
		sts_mtmcode2,
		sts_version,
		sts_hardware_ver,
		0x02,
		'M', //Uplink data interval          for heart-beat uplink
		0x01,
		'S', //Sampling sensor interval      for real-time sensing of MEMS
		0x04,			// dual mode
		0x00,			// service mask
		0x00, 			//reserve01
		0x20,   //32 bytes, below start of p[0] 28 BYTES AND 4 BYTES FALL DOWN CFG
		{		// below 28 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
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		0x00,		//reserve
		},			// above  28 bytes
					// below 4 bytes
		0x01,		//fall_detection_acc_threshold = *10 acceleration measure
		0x03,		//fall detection_depth_threshold  *10cm
		0x00, 		//reserve
		0x02, 		//occupancy over time threshold  *10 minutes
					// below 20 bytes
		{0x0,0x0,0x0,0x0,0x0,   0x0,0x0,0x0,0x0,0x0,    0x0,0x0,0x0,0x0,0x0,   0x0,0x0,0x0,0x0,0x0}
};

#ifdef YUNHORN_STS_O7_ENABLED
extern volatile uint8_t sensor_data_ready;
extern volatile STS_OO_SensorStatusDataTypeDef sts_o7_sensorData;
extern volatile float sts_distance_rss_distance;
volatile uint8_t sts_presence_fall_detection;

#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		HeartBeatPeriodicity
/* USER CODE END PTD */

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

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

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

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

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

/* USER CODE END PM */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* USER CODE BEGIN PFP */

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

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

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

/**
  * @brief  Yunhorn STS Occupancy RSS WakeUP timer callback function
  * @param  context ptr of STS RSS WakeUp context
  */

static void OnYunhornSTSOORSSWakeUpTimerEvent(void *context);

/**
  * @brief  Yunhorn STS Heart beat 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;

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

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

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

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

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

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

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

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

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

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

/**
  * @brief Timer to handle the YunHorn STS Sensor Sampling Process
  */
//static UTIL_TIMER_Object_t YunhornSTSSamplingCheckTimer;
/**
  * @brief Timer to handle the YunHorn STS Sensor 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);

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

  /* USER CODE END LoRaWAN_Init_1 */

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

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

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

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

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

  LmHandlerConfigure(&LmHandlerParams);

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

  /* USER CODE END LoRaWAN_Init_2 */

  LmHandlerJoin(ActivationType, ForceRejoin);

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

    /* USER CODE END LoRaWAN_Init_3 */
  }

  /* USER CODE BEGIN LoRaWAN_Init_Last */
  /// **************************************************************************** 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);
  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);

#ifdef    YUNHORN_STS_O7_ENABLED
  UTIL_TIMER_Create(&YunhornSTSRSSWakeUpTimer,
		  YUNHORN_STS_RSS_WAKEUP_CHECK_TIME,
		  UTIL_TIMER_PERIODIC, 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);


#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)
  {
    case  BUT1_Pin:

    //sts_reed_hall_ext_int = 1;
    UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), CFG_SEQ_Prio_0);
    //sts_reed_hall_ext_int = 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:
#if defined(USE_STM32WLE5)
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStopJoinEvent), CFG_SEQ_Prio_0);
#endif
      break;

    case  BUT3_Pin:
#if defined(USE_STM32WL55)
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStoreContextEvent), CFG_SEQ_Prio_0);
#endif
      break;
    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)
  {
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */

    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, 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");
                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
                }
                else
                {
                  APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n");
                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
                }
              }
              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 uint8_t PrepareSendTxData(void)
{
	uint8_t i = 0;
	uint8_t batteryLevel = GetBatteryLevel();
	uint16_t batteryLevelmV = SYS_GetBatteryLevel();
	SysTime_t occupy_check_time = SysTimeGetMcuTime();
	//sensor_t sensor_data;
	STS_OO_SensorStatusDataTypeDef o7_data;

    STS_PRESENCE_SENSOR_Read(&o7_data);
    o7_data.battery_Pct = (uint8_t)(99*batteryLevel/254);


	// For occupancy over time process
	if ((sts_occupancy_overtime_threshold != 0) && (event_start_time !=0))
	{
		uint32_t check_time_tmp = occupy_check_time.Seconds - event_start_time;

		APP_LOG(TS_OFF, VLEVEL_L, "\r\n Check time at %6u Seconds, time lag =%6u, Started at %6u \r\n", occupy_check_time.Seconds, check_time_tmp, event_start_time);

		if (check_time_tmp > sts_occupancy_overtime_threshold*60)
		{
			sts_occupancy_overtime_state = 1U;
			sts_status_color = STS_RED_BLUE;
			sts_lamp_bar_color = STS_RED_BLUE;
			STS_Lamp_Bar_Refresh();
		}
	} else
	{
		sts_occupancy_overtime_state = 0U;
	}

	// For occupancy over time process


	switch (sts_work_mode) {
		case STS_WIRED_MODE:

			AppData.Buffer[i++] = sts_mtmcode1;
			AppData.Buffer[i++] = sts_mtmcode2;
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//01 Sensor head #1 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.battery_Pct);		//02 Battery Level %
			AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV >>8);			//03 Battery mV MSB
			AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV );			//04 Battery mV LSB
			APP_LOG(TS_OFF, VLEVEL_L,"\r\n######| Mode    S1   BAT %   BAT mV  |"
									"\r\n######|  %1d      %1d    %2d%     %4d mV|\r\n",
									sts_work_mode, AppData.Buffer[2], AppData.Buffer[3], batteryLevelmV);

		break;

		case STS_NETWORK_MODE:
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.lamp_bar_color);  			//01
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.workmode);					//02 WORK MODE

			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//04 Sensor head #2 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//05 Sensor head #3 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//06 Sensor head #4 status
			APP_LOG(TS_OFF, VLEVEL_L,
								"\r\n######| Color  Mode   S1    S2    S3    S4  |"
								"\r\n######|  %1d      %1d      %1d    %1d     %1d     %1d |\r\n",
								AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2],AppData.Buffer[3], AppData.Buffer[4],AppData.Buffer[5]);
		break;

		case STS_REEDSWITCH_MODE:
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.lamp_bar_color);  			//01
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.workmode);					//02 WORK MODE

			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.battery_Pct);		//02 Battery Level %
			AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV >>8);			//03 Battery mV MSB
			AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV );			//04 Battery mV LSB
			APP_LOG(TS_OFF, VLEVEL_L,
				"\r\n######| Color  Mode       S1   VBAT   in mV|"
				"\r\n######|  %1d      %1d         %1d    %2d%    %4d mV|\r\n",
				AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2],AppData.Buffer[3], batteryLevelmV);
		break;

		case   STS_RSS_MODE:
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.lamp_bar_color);  			//01
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.workmode);					//02 WORK MODE

			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//03 Sensor head #2 status
			//AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//   Sensor head #3 status
			//AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//   Sensor head #4 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance>>8);	//04 MSB distance
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance);		//05 LSB distance
			AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score >>8);		//06 MSB score
			AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score);			//07 LSB score
			AppData.Buffer[i++]	= (uint8_t)(0xFF & sts_occupancy_overtime_state);		//08 occupancy over time or not
			APP_LOG(TS_OFF, VLEVEL_L,
									"\r\n######| Color  Mode       S2          |Distance(mm)    MotionScore|"
									"\r\n######|  %1d      %1d          %1d          |%04d             %04d      |\r\n",
									AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2], (uint16_t)o7_data.rss_presence_distance,(uint16_t)o7_data.rss_presence_score);


		break;

		default:
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.lamp_bar_color);  			//01
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.workmode);					//02 WORK MODE

			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//04 Sensor head #2 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//05 Sensor head #3 status
			AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//06 Sensor head #4 status
			if (o7_data.state_sensor2_on_off !=0)
			{
				AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance>>8);	//07 MSB distance
				AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance);		//08 LSB distance
				AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score >>8);		//09 MSB score
				AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score);			//10 LSB score
			}
			AppData.Buffer[i++]	= (uint8_t)(0xFF & sts_occupancy_overtime_state);		//11 occupancy over time or not
			APP_LOG(TS_OFF, VLEVEL_L,
									"\r\n######| Color  Mode   S1    S2    S3    S4  |Distance(mm)    MotionScore|"
									"\r\n######|  %1d      %1d      %1d     %1d     %1d     %1d  |%04d             %04d      |\r\n",
									AppData.Buffer[0], AppData.Buffer[1],AppData.Buffer[2], AppData.Buffer[3],AppData.Buffer[4], AppData.Buffer[5], (uint16_t)o7_data.rss_presence_distance,(uint16_t)o7_data.rss_presence_score);
		break;
	}

	return i;
}

static void SendTxData(void)
{
  /* USER CODE BEGIN SendTxData_1 */
  LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;

  UTIL_TIMER_Time_t nextTxIn = 0;
  uint8_t i = 0;

  if (LmHandlerIsBusy() == false)
  {
    AppData.Port = LORAWAN_USER_APP_PORT;
	//AppData.Buffer[i++] = AppLedStateOn;											//#01

	if (heart_beat_timer != 0L)
	{
			AppData.Port = LORAWAN_USER_APP_HTBT_PORT;
			heart_beat_timer = 0;
			i = PrepareSendTxData();

	} else 	if (sensor_data_ready != 0L)
	{
			sensor_data_ready =0;
			i = PrepareSendTxData();
	}

	if (i!=0)
	{
		AppData.BufferSize = (sts_service_mask >= STS_SERVICE_MASK_L2? 0:i);

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

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

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


  /* USER CODE END SendTxData_1 */
}

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

  /* USER CODE END OnTxTimerEvent_1 */
	//heart_beat_timer = 1;

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

	if (sts_ac_code[0]==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);
		}

	}

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

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

static void OnJoinTimerLedEvent(void *context)
{
  HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); /* LED_RED */
  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 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)
    {
      HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); /* LED_GREEN */
      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);
      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */

      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;

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

    APP_LOG(TS_OFF, VLEVEL_H, "###### U/L FRAME:JOIN | DR:%d | PWR:%d\r\n", joinParams->Datarate, joinParams->TxPower);
  }
  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
  /* USER CODE END OnJoinRequest_1 */
}

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

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

  /* USER CODE END OnSysTimeUpdate_1 */
}

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

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

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

  /* USER CODE BEGIN OnMacProcessNotify_2 */

  /* USER CODE END OnMacProcessNotify_2 */
}

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

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

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

  HeartBeatPeriodicity = TxPeriodicity;

  /* 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_L,"**************** 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 */
  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 */
  /* 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 */
  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 */
  /* USER CODE END OnStopJoinTimerEvent_Last */
}

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

  /* USER CODE BEGIN StoreContext_1 */

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

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

  /* USER CODE END StoreContext_Last */
}

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

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

  /* USER CODE END OnNvmDataChange_Last */
}

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

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

  /* USER CODE END OnStoreContextRequest_Last */
}

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

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

  /* USER CODE END OnRestoreContextRequest_Last */
}

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

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

	}
}

/**
  * @brief  Yunhorn STS Heart beat timer callback function
  * @param  context ptr of context
  */

static void OnYunhornSTSHeartBeatTimerEvent(void *context)
{
	heart_beat_timer = 1;

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

	}
}

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

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

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

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

  /* Update timer periodicity */

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

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

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

#ifdef    YUNHORN_STS_O7_ENABLED
  UTIL_TIMER_Stop(&YunhornSTSRSSWakeUpTimer);
  UTIL_TIMER_SetPeriod(&YunhornSTSRSSWakeUpTimer, SamplingPeriodicity);
  UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);
  APP_LOG(TS_OFF, VLEVEL_L,"**************** 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_L,"**************** 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);
    }
}
*/

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


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

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

	if (((char)tlv_buf[CFG_CMD1] == 'Y') && (tlv_buf_size <=5))  // BEGIN OF *** BOARD LEVEL CONTROL OR REPORT
	{
		switch ((char)tlv_buf[CFG_CMD2])
		{
			case 'Z':		//"YZ"
				if ((char)tlv_buf[CFG_CMD3] == 'H') {  //BOARD SOFT RESET, REVIVE "YZH"
					//BOARD REVIVE

					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, 20, "!YunHorn STS Revive!");
					HAL_Delay(5000);
					APP_LOG(TS_OFF, VLEVEL_H, "\r\n Yunhorn STS Node Revive ... \r\n");
					HAL_Delay(3000);

					OnSystemReset();

				}	else if 	((char)tlv_buf[CFG_CMD3] == 'S') {		// Self Function Testing "YZS"
					i=0;
					//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, outbuf);

					i=0;
					memset(outbuf,sizeof(outbuf),0x30);

					STS_SENSOR_Function_Test_Process();

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

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

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

					STS_SENSOR_Distance_Test_Process();

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

					i=0;
					memset(outbuf,sizeof(outbuf),0x30);
					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));
					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;
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, 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, 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;
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, outbuf);
					APP_LOG(TS_OFF, VLEVEL_M, "###### YUNHORN Report Version [ %10x ] \r\n", outbuf);
				}
				else
				if ((char)tlv_buf[CFG_CMD3] == 'C') { // "YVC"   REPORT NVM STORED CONFIG PARAMETERS  "YVC"

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

					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]/10);  	//UPLINK Periodicity count high
					outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_PERIODICITY]%10);  	//UPLINK Periodicity count low
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_UNIT];  				//UPLINK Periodicity unit
					outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_SAMPLING]/10);  		//SAMPLING Periodicity count high
					outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_SAMPLING]%10);  		//SAMPLING Periodicity count low
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_S_UNIT];  			//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_RESERVE01];  		//service mask
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_LEN];  				//length of following cfg value

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

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

		    case 'F':
					// TODO # of modules
						if (((uint8_t)(tlv_buf[CFG_CMD3]-0x30) >= 0) && ((uint8_t)tlv_buf[CFG_CMD3]-0x30) <=9) {
						STS_SENSOR_Power_OFF((tlv_buf[CFG_CMD3]-0x30));
						i=0;
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD3];
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, outbuf);
					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;
			case 'H':
					// TODO # of modules
						if (((uint8_t)(tlv_buf[CFG_CMD3]-0x30) >= 0) && ((uint8_t)tlv_buf[CFG_CMD3]-0x30) <=9) {
						STS_SENSOR_MEMS_Reset((tlv_buf[CFG_CMD3]-0x30));
						i=0;
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD3];
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, 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;
						}
						TxPeriodicity = periodicity_length*1000;	//translate to 1000ms=1s
						HeartBeatPeriodicity = TxPeriodicity;

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

						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, 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 sampling_periodicity_length = (tlv_buf[CFG_CMD3]-0x30)*10+ (tlv_buf[CFG_CMD4]-0x30);

						if ((char)tlv_buf[CFG_CMD5] == 'M') {
							sampling_periodicity_length *= 60;
						} else if ((char)tlv_buf[CFG_CMD5] == 'H') {
							sampling_periodicity_length *= 3600;
						}
						SamplingPeriodicity = sampling_periodicity_length*1000;  //translate to 1000ms=1s
						OnYunhornSTSSamplingPeriodicityChanged(SamplingPeriodicity);

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

						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, 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 Sampling Interval Changed to [ %d ] Seconds\r\n", sampling_periodicity_length);

					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;


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

		//i = P_MEM_CFG;		//start of parameter

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

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

				uint8_t j=0;
				if (mems_ver == sts_version)
				{					// Firmware version or Variation of MEMS/component
					if (tlv_buf_size == 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);
							APP_LOG(TS_OFF, VLEVEL_M, "\r\n Cloud Color Set to %d \r\n",sts_cloud_netcolor);
						}
						sts_service_mask = STS_SERVICE_MASK_L0;
						sts_lamp_bar_color = sts_cloud_netcolor;
						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
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD3];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD4];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD5];

						STS_Combined_Status_Processing();
					} else if  (tlv_buf_size == 7 && tlv_buf[CFG_CMD4]=='F')  // Change fall detection, occupancy overtime threshold
					{
						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')))
						{
							sts_fall_detection_acc_threshold 	= (uint8_t)(tlv_buf[CFG_CMD5] - 0x30)*10;		//acc *10 mg/s2
							sts_fall_detection_depth_threshold 	= (uint8_t)(tlv_buf[CFG_CMD6] - 0x30)*10;			//depth *10 in cm
							sts_occupancy_overtime_threshold 	= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30)*10;			// 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_detection_reserve			= 0x0;
							sts_cfg_nvm.occupancy_overtime_threshold 	= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30);

							if ((sts_fall_detection_acc_threshold ==0)&&(sts_fall_detection_depth_threshold==0))
							{
								sts_presence_fall_detection = 0;
							}

							OnStoreSTSCFGContextRequest();

							i=0;						// Step 1: Prepare status update message
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD1];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD2];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD3];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD4];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD5];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD6];
							outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD7];

							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
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD3];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD4];

						//sts_service_mask = STS_SERVICE_MASK_L0;
						//sts_lamp_bar_color = STS_GREEN;
						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();
						} 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; 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;
								}
							}

							if (invalid_flag != 1)
							{
								STS_PRESENCE_SENSOR_NVM_CFG();

								i=0;						// Step 1: Prepare status update message

								outbuf[i++] 	= (char) 'P';
								outbuf[i++] 	= sts_mtmcode1;
								outbuf[i++] 	= sts_mtmcode2;
								outbuf[i++] 	= sts_version;
								outbuf[i++] 	= sts_hardware_ver;

								for (uint8_t j=0; j < CFG_CMD_RSS_FULL_SIZE; j++)
								{
									//outbuf[i++] = (uint8_t)(sts_cfg_nvm.p[j]+0x30)&0xff;
									outbuf[i++]	=	tlv_buf[CFG_CMD4+j];
								}

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

							for (j =0; j < CFG_CMD_RSS_SIMPLE_SIZE; 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;
								}
							}

							if (invalid_flag != 1)
							{
								STS_PRESENCE_SENSOR_NVM_CFG_SIMPLE();

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

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

								// Step 2: Save valid config to NVM
								sts_cfg_nvm.mtmcode1 = sts_mtmcode1;
								sts_cfg_nvm.mtmcode2 = sts_mtmcode2;
								sts_cfg_nvm.version = sts_version;
								sts_cfg_nvm.hardware_ver = sts_hardware_ver;
								sts_cfg_nvm.work_mode = sts_work_mode;
								sts_cfg_nvm.sts_service_mask = sts_service_mask;
								sts_cfg_nvm.length = CFG_CMD_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, outbuf);
				}
				}

			break;

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

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

			break;
			default:
			// for multiple sensor heads or MEMS components, TODO   2022-10-21 PARK HERE
				//STS_SENSOR_Upload_Config_Invalid_Message();
			break;
		}   //END OF SWITCH TVL_BUF_P_MEMS_NO
		 //END  OF PARAMETER CONFIG
	} else if (((char)tlv_buf[CFG_CMD1] == 'A') && ((char)tlv_buf[CFG_CMD2] == 'C') && (tlv_buf_size == (YUNHORN_STS_AC_CODE_SIZE+2)))  // BEGIN OF *** BOARD LEVEL AUTHORIZATION CODE
	{
		// 'AC'+ AC_CODE(20bytes)

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

		STS_YUNHORN_RFAC_HANDLE_PROCESS();

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

			sts_service_mask = STS_SERVICE_MASK_L0;

			sts_cfg_nvm.sts_service_mask = sts_service_mask;
			OnStoreSTSCFGContextRequest();


		} else {
			sts_service_mask = STS_SERVICE_MASK_L2;

		}

		i=0;
		for (uint8_t j=0; j <(tlv_buf_size) ; j++) {
			outbuf[i++] = (uint8_t) tlv_buf[j];
		}
		STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (char*)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, "PVXXX");
}

static void STS_YUNHORN_RFAC_HANDLE_PROCESS(void)
{

	STS_YunhornAuthenticationCode_Process();

}

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

	if (LmHandlerIsBusy() == false)
	{

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

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

		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);
	      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
	    }

		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]="";

#ifdef YUNHORN_STS_O7_ENABLED
	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.reseve01;
	nvm_store_value[i++] = (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.fall_detection_acc_threshold;
	nvm_store_value[i++] = sts_cfg_nvm.fall_detection_depth_threshold;
	nvm_store_value[i++] = sts_cfg_nvm.fall_detection_reserve;
	nvm_store_value[i++] = sts_cfg_nvm.occupancy_overtime_threshold;

	for (j = 0; j < YUNHORN_STS_AC_CODE_SIZE; j++) {
		nvm_store_value[i++] = (sts_cfg_nvm.ac[j]);
	}
#endif

  /* USER CODE END OnStoreContextRequest_1 */
  /* store nvm in flash */

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


  /* USER CODE BEGIN OnStoreContextRequest_Last */


  /* USER CODE END OnStoreContextRequest_Last */
}

void OnRestoreSTSCFGContextRequest(uint8_t *cfg_in_nvm)
{
  /* USER CODE BEGIN OnRestoreSTSCFGContextRequest_1 */
//	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 */
  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);
  /* USER CODE BEGIN OnRestoreContextRequest_Last */

#ifdef YUNHORN_STS_O7_ENABLED
	if ((nvm_stored_value[NVM_MTM1] != sts_mtmcode1) || (nvm_stored_value[NVM_MTM2] != sts_mtmcode2) || (nvm_stored_value[NVM_VER] != sts_version))
	{
		APP_LOG(TS_OFF, VLEVEL_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.reseve01			= (uint8_t)(nvm_stored_value[NVM_RESERVE01]);
			sts_cfg_nvm.length 				= (uint8_t)(nvm_stored_value[NVM_LEN]&0x3F);		//MAX 32 bytes

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

			sts_cfg_nvm.fall_detection_acc_threshold 	= (uint8_t)nvm_stored_value[NVM_FALL_DETECTION_ACC_THRESHOLD];
			sts_cfg_nvm.fall_detection_depth_threshold 	= (uint8_t)nvm_stored_value[NVM_FALL_DETECTION_DEPTH_THRESHOLD];
			sts_cfg_nvm.fall_detection_reserve			= (uint8_t)nvm_stored_value[NVM_FALL_DETECTION_RESERVE];
			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 = (periodicity > 10)? periodicity : 10;		// in seconds unit
	TxPeriodicity= periodicity*1000;  // to ms
	OnTxPeriodicityChanged(TxPeriodicity);				// in msec unit

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

	samplingperiodicity = (samplingperiodicity > 0)? samplingperiodicity : 1;		// in seconds unit
	OnYunhornSTSSamplingPeriodicityChanged(samplingperiodicity*1000);				// in m-sec unit

	sts_work_mode	= sts_cfg_nvm.work_mode;
	sts_lamp_bar_color = STS_GREEN;
	sts_service_mask = sts_cfg_nvm.sts_service_mask;

	sts_fall_detection_acc_threshold 	= (uint8_t)sts_cfg_nvm.fall_detection_acc_threshold*10;
	sts_fall_detection_depth_threshold 	= (uint8_t)sts_cfg_nvm.fall_detection_depth_threshold*10;		//in cm
	// ****								= sts_cfg_nvm.fall_detection_reserve;
	sts_occupancy_overtime_threshold 	= (uint8_t)sts_cfg_nvm.occupancy_overtime_threshold*10;			// minutes


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


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

}

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

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

}

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

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


	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
	#ifdef YUNHORN_STS_O7_ENABLED
		uint8_t self_test_result[10]={0,0,0,0,0, 0,0,0,0,0};

		STS_PRESENCE_SENSOR_Function_Test_Process(self_test_result, 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
	}

	memset((void*)outbuf,sizeof(outbuf),0x30);
	memcpy((void*)outbuf, tstbuf, i);
	//STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, outbuf);

}


/*

			if (sts_work_mode == STS_WIRED_MODE)
				{
					AppData.Buffer[i++] = sts_mtmcode1;
					AppData.Buffer[i++] = sts_mtmcode2;
					AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//01 Sensor head #1 status
					AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.battery_Pct);		//02 Battery Level %
					AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV >>8);			//03 Battery mV MSB
					AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV );			//04 Battery mV LSB
					APP_LOG(TS_OFF, VLEVEL_L,"\r\n######| Mode    S1   BAT %   BAT mV  |"
							"\r\n######|  %1d      %1d    %2d%     %4d mV|\r\n",
							sts_work_mode, AppData.Buffer[2], AppData.Buffer[3], batteryLevelmV);

				} else
				{
					AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.lamp_bar_color);  			//01
					AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.workmode);					//02 WORK MODE

					if (sts_work_mode == STS_NETWORK_MODE)
					{
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//04 Sensor head #2 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//05 Sensor head #3 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//06 Sensor head #4 status
						APP_LOG(TS_OFF, VLEVEL_L,
								"\r\n######| Color  Mode   S1    S2    S3    S4  |"
								"\r\n######|  %1d      %1d      %1d    %1d     %1d     %1d |\r\n",
								AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2],AppData.Buffer[3], AppData.Buffer[4],AppData.Buffer[5]);
					} else	if (sts_work_mode == STS_REEDSWITCH_MODE)
					{
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.battery_Pct);		//02 Battery Level %
						AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV >>8);			//03 Battery mV MSB
						AppData.Buffer[i++] = (uint8_t)(0xFF & batteryLevelmV );			//04 Battery mV LSB
						APP_LOG(TS_OFF, VLEVEL_L,
								"\r\n######| Color  Mode       S1   VBAT   in mV|"
								"\r\n######|  %1d      %1d         %1d    %2d%    %4d mV|\r\n",
								AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2],AppData.Buffer[3], batteryLevelmV);

					} else if (sts_work_mode == STS_RSS_MODE) {
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//03 Sensor head #2 status
						//AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//   Sensor head #3 status
						//AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//   Sensor head #4 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance>>8);	//04 MSB distance
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance);		//05 LSB distance
						AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score >>8);		//06 MSB score
						AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score);			//07 LSB score
						AppData.Buffer[i++]	= (uint8_t)(0xFF & sts_occupancy_overtime_state);		//11 occupancy over time or not
						APP_LOG(TS_OFF, VLEVEL_L,
								"\r\n######| Color  Mode       S2          |Distance(mm)    MotionScore|"
								"\r\n######|  %1d      %1d          %1d          |%04d             %04d      |\r\n",
								AppData.Buffer[0], AppData.Buffer[1], AppData.Buffer[2], (uint16_t)o7_data.rss_presence_distance,(uint16_t)o7_data.rss_presence_score);
					} else {
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor1_on_off);		//03 Sensor head #1 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor2_on_off);		//04 Sensor head #2 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor3_on_off);		//05 Sensor head #3 status
						AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.state_sensor4_on_off);		//06 Sensor head #4 status
						if (o7_data.state_sensor2_on_off !=0)
						{
							AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance>>8);	//07 MSB distance
							AppData.Buffer[i++] = (uint8_t)(0xFF & o7_data.rss_presence_distance);		//08 LSB distance
							AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score >>8);		//09 MSB score
							AppData.Buffer[i++]	= (uint8_t)(0xFF & o7_data.rss_presence_score);			//10 LSB score
						}
						// overtime dismissed while state not back to zero
						if ((o7_data.state_sensor1_on_off == 0)&& (o7_data.state_sensor2_on_off == 0))
								sts_occupancy_overtime_state = 0;
						AppData.Buffer[i++]	= (uint8_t)(0xFF & sts_occupancy_overtime_state);		//11 occupancy over time or not
						APP_LOG(TS_OFF, VLEVEL_L,
								"\r\n######| Color  Mode   S1    S2    S3    S4  |Distance(mm)    MotionScore|"
								"\r\n######|  %1d      %1d      %1d     %1d     %1d     %1d  |%04d             %04d      |\r\n",
								AppData.Buffer[0], AppData.Buffer[1],AppData.Buffer[2], AppData.Buffer[3],AppData.Buffer[4], AppData.Buffer[5], (uint16_t)o7_data.rss_presence_distance,(uint16_t)o7_data.rss_presence_score);
					}

				}



 */