/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    lora_app.c
  * @author  MCD Application Team
  * @brief   Application of the LRWAN Middleware
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 STMicroelectronics.
  * 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 "opt3001.h"
#include "i2c.h"
#include "hdc2080.h"
#include "tim.h"
#include "adc.h"
#include "app_x-cube-ai.h"
/* USER CODE END Includes */

/* External variables ---------------------------------------------------------*/
/* USER CODE BEGIN EV */
uint16_t old_capture = 0;
uint16_t new_capture = 0;
uint32_t diffCapture = 0;
uint32_t impedance = 0;
uint16_t watermark = 0;
uint8_t prediction = 0;
ai_float output_value = 0;
uint8_t wave_cnt = 0;
//uint8_t flag_var_dc = 0; // flag that checks if the LoRa DC has been modified
uint32_t new_lora_dc = APP_TX_DUTYCYCLE; // variable where the new DC is stored
uint8_t flag_meas = 0;
//float fr_tmp_lora = 0;

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

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

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

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

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

  MX_X_CUBE_AI_Init();
  APP_LOG(TS_OFF, VLEVEL_M, "X-CUBE-AI initialized\r\n");

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

  /* 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:
//      /* 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;
//    default:
//      break;
  }
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if (htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_1)
	{
		old_capture = new_capture;
		new_capture = TIM1 -> CCR1;
//		if (wave_cnt < 3)
//		{
//			wave_cnt++;
//		}
	}
	if (htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_2)
	{
		old_capture = new_capture;
		new_capture = TIM1 -> CCR2;
//		if (wave_cnt < 3)
//		{
//			wave_cnt++;
//		}
	}
}

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
	{
//		flag_tim = 1;
//		HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1);
		flag_meas = 1;
		HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1); // Stop Timer 1 in IC with Interrupt
		HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_2); // Stop Timer 2 in IC with Interrupt

//		HAL_GPIO_WritePin(GPIOA, IMP_EN_Pin, GPIO_PIN_RESET); //Stop Impedance Modulus
//		HAL_GPIO_WritePin(GPIOC, EN_WT_Pin, GPIO_PIN_RESET); //Stop Watermark Measurement

	}
}

/* USER CODE END PB_Callbacks */

/* Private functions ---------------------------------------------------------*/
/* USER CODE BEGIN PrFD */
uint32_t eval_freq()
{
	uint32_t freq = 0;
	float fr_tmp;

	if(old_capture != new_capture)
	{
		if(new_capture > old_capture)
		{
			diffCapture = new_capture - old_capture;
			fr_tmp = 2000000 / diffCapture;
			freq = (uint32_t) fr_tmp;
		}
		else
		{
			diffCapture = 65535 - old_capture + new_capture;
			fr_tmp = 2000000 / diffCapture;
			freq = (uint32_t) fr_tmp;
		}
	}

//		freq = 2000000 / diffCapture; /* Evaluate the frequency */


	return freq;
}

// Enable and read ADC in 7
uint8_t sen_bz_1()
{
	uint8_t sen_1 = 0;
	HAL_GPIO_WritePin(GPIOB, EN_SEN_3_Pin, GPIO_PIN_SET); // Set pin to power voltage divider on adc in 7
	HAL_Delay(200);
	ADC_ChannelConfTypeDef sConfig = {0};
	sConfig.Channel = ADC_CHANNEL_7;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}

	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	sen_1 = HAL_ADC_GetValue(&hadc);
	HAL_GPIO_WritePin(GPIOB, EN_SEN_3_Pin, GPIO_PIN_RESET); // Set pin to STOP power voltage divider on adc in 7

	return sen_1;
}

// Enable and read ADC in 2
uint8_t sen_bz_2()
{
	uint8_t sen_2 = 0;
	HAL_GPIO_WritePin(GPIOC, EN_SEN_1_Pin, GPIO_PIN_SET); // Set pin to power voltage divider on adc in 7
	HAL_Delay(200);
	ADC_ChannelConfTypeDef sConfig = {0};
	sConfig.Channel = ADC_CHANNEL_2;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}

	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	sen_2 = HAL_ADC_GetValue(&hadc);
	HAL_GPIO_WritePin(GPIOC, EN_SEN_1_Pin, GPIO_PIN_RESET); // Set pin to STOP power voltage divider on adc in 7

	return sen_2;
}

// Enable and read ADC in 3

uint8_t sen_bz_3()
{
	uint8_t sen_3 = 0;
	HAL_GPIO_WritePin(GPIOA, EN_SEN_4_Pin, GPIO_PIN_SET); // Set pin to power voltage divider on adc in 7
	HAL_Delay(200);
	ADC_ChannelConfTypeDef sConfig = {0};
	sConfig.Channel = ADC_CHANNEL_3;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}

	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	sen_3 = HAL_ADC_GetValue(&hadc);
	HAL_GPIO_WritePin(GPIOA, EN_SEN_4_Pin, GPIO_PIN_RESET); // Set pin to STOP power voltage divider on adc in 7

	return sen_3;
}

// Enable and read ADC in 1
uint8_t sen_bz_4()
{
	uint8_t sen_4 = 0;
	HAL_GPIO_WritePin(GPIOB, EN_SEN_2_Pin, GPIO_PIN_SET); // Set pin to power voltage divider on adc in 7
	HAL_Delay(200);
	ADC_ChannelConfTypeDef sConfig = {0};
	sConfig.Channel = ADC_CHANNEL_1;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}

	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	sen_4 = HAL_ADC_GetValue(&hadc);
	HAL_GPIO_WritePin(GPIOB, EN_SEN_2_Pin, GPIO_PIN_RESET); // Set pin to STOP power voltage divider on adc in 7

	return sen_4;
}
/* 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;
                }
              }
              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 */
//                }
                if (appData -> Buffer[0] == 0) // Receiving 1 bye on PORT 2 byte equal to 0 resets the MCU
                {
                	NVIC_SystemReset();
                }
              }
              if (appData->BufferSize == 2) // Receiving 2 bytes on PORT 2 changes the LoRa DC
              {
            	  new_lora_dc =  (((appData -> Buffer[0]) << 8) | (appData -> Buffer[1]))  * 1000; //The new LoRa DC is in SECONDS
            	  OnTxPeriodicityChanged(new_lora_dc);
              }
              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 */
//	float temp = 0;
//	float rh = 0;
//	float lux = 0;

	uint8_t sen_bz_1_val = 0;
	uint8_t sen_bz_2_val = 0;
	uint8_t sen_bz_3_val = 0;
	uint8_t sen_bz_4_val = 0;

	uint8_t opt_ok = 0;

	uint16_t rh_int = 0;
	uint16_t lux_int = 0;
	uint16_t temp_int = 0;

	uint32_t *output_value_ptr;

	APP_LOG(TS_OFF, VLEVEL_H, "-->\tFino a qui\r\n");

//	uint8_t temp_neg = 0;
	// READ ADC inputs
	MX_ADC_Init();
//	sen_bz_1_val = sen_bz_1();
//	sen_bz_2_val = sen_bz_2();
//	sen_bz_3_val = sen_bz_3();
//	sen_bz_4_val = sen_bz_4();
	HAL_ADC_Stop(&hadc);
//	HAL_ADC_DeInit(&hadc);
//	HAL_ADC_MspDeInit(&hadc);

	MX_I2C2_Init();
	MX_TIM1_Init();
	MX_TIM2_Init();
	new_capture = 0;
	old_capture = 0;

//	uint16_t temperature = 0;
//	uint16_t rel_hum = 0;

//	temp = hdc2080_calculate_temp()*100; // Read hdc2080 temp, 100* used to recast into a int one
	temp_int = hdc2080_readtemp();
//	rh = hdc2080_calculate_rh()*100; // Read hdc2080 rh, 100* used to recast into a int one
	rh_int = hdc2080_readrh();

	if (read_devid() == 12289) // checks if the opt3001 is actually conntected
	{
//		lux = calculate_lux(); // Read opt3001 visible light intensity
		opt_ok = opt3001_init();
		if (opt_ok == 1)
		{
			lux_int = opt3001_readdata();
		}
	}
	else
	{
		lux_int = 0;
	}

//	temp = -2450;
//	rh = 4500;
//	lux = 11250;

//	rh_int = (uint16_t)rh;
//	lux_int = (uint16_t)lux;

//	if (temp < 0)
//	{
//		temp_neg = 1;
//		temp = temp * (-1);
//	}
//	temp_int = (int)temp;
	flag_meas = 0;
	TIM2 -> CNT = 0; // Reset Timer 2 Counter
	TIM1 -> CNT = 0; // Reset Timer 1 Counter
	HAL_TIM_IC_Init(&htim1); // Initialize Timer 1
	HAL_TIM_OC_Init(&htim2); // Initialize Timer 2

//	HAL_GPIO_WritePin(GPIOA, IMP_EN_Pin, GPIO_PIN_SET); //Start Impedance Modulus
	HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); // Start Timer 1 CH1 in IC with Interrupt
	HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1); // Start Timer 2 CH1 in OC con Interrupt
	HAL_GPIO_WritePin(GPIOA, IMP_EN_Pin, GPIO_PIN_SET); //Start Impedance Modulus
//	HAL_Delay(1500);
	while(flag_meas == 0)
	{

	}
	HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1); // Stop Timer 1 in IC with Interrupt
	HAL_GPIO_WritePin(GPIOA, IMP_EN_Pin, GPIO_PIN_RESET); //Stop Impedance Modulus
//	HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1); // Stop Timer 1 in IC with Interrupt
	HAL_TIM_OC_Stop_IT(&htim2, TIM_CHANNEL_1); // Start Timer 2 CH1 in OC con Interrupt

	HAL_GPIO_WritePin(GPIOA, IMP_EN_Pin, GPIO_PIN_RESET); //Stop Impedance Modulus

	impedance = eval_freq(); // Evaluate the frequency relative to stem impedance modulus
	flag_meas = 0;

	TIM1 -> CNT = 0; // Reset timer 1 counter
	TIM2 -> CNT = 0; // Reset timer 2 counter
	HAL_GPIO_WritePin(GPIOC, EN_WT_Pin, GPIO_PIN_SET); //Start Watermark Measurement
	HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); // Start Timer 1 CH2 in IC with Interrupt
	HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1); // Start Timer 2 CH1 in OC con Interrupt
	while(flag_meas == 0)
	{

	}
	flag_meas = 0;
//	HAL_Delay(1500);
	HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_2); // Stop Timer 1 CH2 in IC with Interrupt
	HAL_GPIO_WritePin(GPIOC, EN_WT_Pin, GPIO_PIN_RESET); //Stop Watermark Measurement
//	HAL_TIM_OC_Stop_IT(&htim2, TIM_CHANNEL_1); // Start Timer 2 CH1 in OC con Interrupt

	TIM1 -> CNT = 0; // Reset timer 1 counter
	TIM2 -> CNT = 0; // Reset Timer 2 counter
	watermark = (uint16_t) (eval_freq());

	MX_X_CUBE_AI_Process();
	output_value_ptr=(uint32_t*)(&output_value);

  LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;
//  uint8_t batteryLevel = GetBatteryLevel();
//  sensor_t sensor_data;
  UTIL_TIMER_Time_t nextTxIn = 0;

  if (LmHandlerIsBusy() == false)
  {
#ifdef CAYENNE_LPP
    uint8_t channel = 0;
#else
//    uint16_t pressure = 0;
//    int16_t temperature = 0;
//    uint16_t humidity = 0;
    uint32_t i = 0;
//    int32_t latitude = 0;
//    int32_t longitude = 0;
//    uint16_t altitudeGps = 0;
#endif /* CAYENNE_LPP */

//    EnvSensors_Read(&sensor_data);
//
//    APP_LOG(TS_ON, VLEVEL_M, "VDDA: %d\r\n", batteryLevel);
//    APP_LOG(TS_ON, VLEVEL_M, "temp: %d\r\n", (int16_t)(sensor_data.temperature));

    AppData.Port = LORAWAN_USER_APP_PORT;

#ifdef CAYENNE_LPP
    CayenneLppReset();
    CayenneLppAddBarometricPressure(channel++, sensor_data.pressure);
    CayenneLppAddTemperature(channel++, sensor_data.temperature);
    CayenneLppAddRelativeHumidity(channel++, (uint16_t)(sensor_data.humidity));

    if ((LmHandlerParams.ActiveRegion != LORAMAC_REGION_US915) && (LmHandlerParams.ActiveRegion != LORAMAC_REGION_AU915)
        && (LmHandlerParams.ActiveRegion != LORAMAC_REGION_AS923))
    {
      CayenneLppAddDigitalInput(channel++, GetBatteryLevel());
      CayenneLppAddDigitalOutput(channel++, AppLedStateOn);
    }

    CayenneLppCopy(AppData.Buffer);
    AppData.BufferSize = CayenneLppGetSize();
#else  /* not CAYENNE_LPP */
//    humidity    = (uint16_t)(sensor_data.humidity * 10);            /* in %*10     */
//    temperature = (int16_t)(sensor_data.temperature);
//    pressure = (uint16_t)(sensor_data.pressure * 100 / 10); /* in hPa / 10 */
//
//    AppData.Buffer[i++] = AppLedStateOn;
//    AppData.Buffer[i++] = (uint8_t)((pressure >> 8) & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(pressure & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(temperature & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)((humidity >> 8) & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(humidity & 0xFF);

    if ((LmHandlerParams.ActiveRegion == LORAMAC_REGION_US915) || (LmHandlerParams.ActiveRegion == LORAMAC_REGION_AU915)
        || (LmHandlerParams.ActiveRegion == LORAMAC_REGION_AS923))
    {
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
    }
    else
    {
//      latitude = sensor_data.latitude;
//      longitude = sensor_data.longitude;

//      AppData.Buffer[i++] = GetBatteryLevel();        /* 1 (very low) to 254 (fully charged) */
//      AppData.Buffer[i++] = (uint8_t)((latitude >> 16) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((latitude >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(latitude & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((longitude >> 16) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((longitude >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(longitude & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((altitudeGps >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(altitudeGps & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)((rh_int >> 8) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(rh_int & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)((lux_int >> 8) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(lux_int & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)((temp_int >> 8) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(temp_int & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)((impedance >> 16) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)((impedance >> 8) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(impedance & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)((watermark >> 8) & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(watermark & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)(prediction & 0xFF);

    	AppData.Buffer[i++] = (uint8_t)(((uint8_t*)output_value_ptr)[3] & 0xFF);
    	AppData.Buffer[i++] = (uint8_t)(((uint8_t*)output_value_ptr)[2] & 0xFF);
		AppData.Buffer[i++] = (uint8_t)(((uint8_t*)output_value_ptr)[1] & 0xFF);
		AppData.Buffer[i++] = (uint8_t)(((uint8_t*)output_value_ptr)[0] & 0xFF);

//    	AppData.Buffer[i++] = (uint8_t)(sen_bz_1_val & 0xFF);
//    	AppData.Buffer[i++] = (uint8_t)(sen_bz_2_val & 0xFF);
//    	AppData.Buffer[i++] = (uint8_t)(sen_bz_3_val & 0xFF);
//    	AppData.Buffer[i++] = (uint8_t)(sen_bz_4_val & 0xFF);

//    	AppData.Buffer[i++] = (uint8_t)(temp_neg & 0xFF);

    }

    AppData.BufferSize = i;
#endif /* CAYENNE_LPP */

    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_SetPeriod(&TxTimer, MAX(nextTxIn, new_lora_dc));
    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)
{
//  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 */
}

/* 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");
      }
    }
    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);
  }
  /* USER CODE END OnJoinRequest_1 */
}

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

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

  /* USER CODE END OnSysTimeUpdate_1 */
}

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

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

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

  /* USER CODE BEGIN OnMacProcessNotify_2 */

  /* USER CODE END OnMacProcessNotify_2 */
}

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

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

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

  /* Update timer periodicity */
  UTIL_TIMER_Stop(&TxTimer);
  UTIL_TIMER_SetPeriod(&TxTimer, TxPeriodicity);
  UTIL_TIMER_Start(&TxTimer);
  /* USER CODE BEGIN OnTxPeriodicityChanged_2 */

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

