/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file lora_app.c * @author MCD Application Team * @brief Application of the LRWAN Middleware ****************************************************************************** * @attention * *

© Copyright (c) 2020 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "platform.h" #include "Region.h" /* Needed for LORAWAN_DEFAULT_DATA_RATE */ #include "sys_app.h" #include "lora_app.h" #include "stm32_seq.h" #include "stm32_timer.h" #include "utilities_def.h" #include "lora_app_version.h" #include "lorawan_version.h" #include "subghz_phy_version.h" #include "lora_info.h" #include "LmHandler.h" #include "stm32_lpm.h" #include "adc_if.h" #include "sys_conf.h" #include "CayenneLpp.h" #include "sys_sensors.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* External variables ---------------------------------------------------------*/ /* USER CODE BEGIN EV */ /* 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 ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* 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 tx event callback function * @param params status of last Tx */ static void OnTxData(LmHandlerTxParams_t *params); /** * @brief callback when LoRa 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); /*! * Will be called each time a Radio IRQ is handled by the MAC layer * */ static void OnMacProcessNotify(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 ---------------------------------------------------------*/ static ActivationType_t ActivationType = LORAWAN_DEFAULT_ACTIVATION_TYPE; /** * @brief LoRaWAN handler Callbacks */ static LmHandlerCallbacks_t LmHandlerCallbacks = { .GetBatteryLevel = GetBatteryLevel, .GetTemperature = GetTemperatureLevel, .GetUniqueId = GetUniqueId, .GetDevAddr = GetDevAddr, .OnMacProcess = OnMacProcessNotify, .OnJoinRequest = OnJoinRequest, .OnTxData = OnTxData, .OnRxData = OnRxData }; /** * @brief LoRaWAN handler parameters */ static LmHandlerParams_t LmHandlerParams = { .ActiveRegion = ACTIVE_REGION, .DefaultClass = LORAWAN_DEFAULT_CLASS, .AdrEnable = LORAWAN_ADR_STATE, .TxDatarate = LORAWAN_DEFAULT_DATA_RATE, .PingPeriodicity = LORAWAN_DEFAULT_PING_SLOT_PERIODICITY }; /** * @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; /* 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_1 */ LED_Init(LED_GPIO_PORT,LED_BLUE); LED_Init(LED_GPIO_PORT,LED_RED1); LED_Init(LED_GPIO_PORT,LED_RED2); /* Get LoRa APP version*/ APP_LOG(TS_OFF, VLEVEL_M, "APP_VERSION: V%X.%X.%X\r\n", (uint8_t)(__LORA_APP_VERSION >> __APP_VERSION_MAIN_SHIFT), (uint8_t)(__LORA_APP_VERSION >> __APP_VERSION_SUB1_SHIFT), (uint8_t)(__LORA_APP_VERSION >> __APP_VERSION_SUB2_SHIFT)); /* Get MW LoraWAN info */ APP_LOG(TS_OFF, VLEVEL_M, "MW_LORAWAN_VERSION: V%X.%X.%X\r\n", (uint8_t)(__LORAWAN_VERSION >> __APP_VERSION_MAIN_SHIFT), (uint8_t)(__LORAWAN_VERSION >> __APP_VERSION_SUB1_SHIFT), (uint8_t)(__LORAWAN_VERSION >> __APP_VERSION_SUB2_SHIFT)); /* 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 >> __APP_VERSION_MAIN_SHIFT), (uint8_t)(__SUBGHZ_PHY_VERSION >> __APP_VERSION_SUB1_SHIFT), (uint8_t)(__SUBGHZ_PHY_VERSION >> __APP_VERSION_SUB2_SHIFT)); UTIL_TIMER_Create(&TxLedTimer, 0xFFFFFFFFU, UTIL_TIMER_ONESHOT, OnTxTimerLedEvent, NULL); UTIL_TIMER_Create(&RxLedTimer, 0xFFFFFFFFU, UTIL_TIMER_ONESHOT, OnRxTimerLedEvent, NULL); UTIL_TIMER_Create(&JoinLedTimer, 0xFFFFFFFFU, UTIL_TIMER_PERIODIC, OnJoinTimerLedEvent, NULL); UTIL_TIMER_SetPeriod(&TxLedTimer, 500); UTIL_TIMER_SetPeriod(&RxLedTimer, 500); UTIL_TIMER_SetPeriod(&JoinLedTimer, 500); /* USER CODE END LoRaWAN_Init_1 */ UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LmHandlerProcess), UTIL_SEQ_RFU, LmHandlerProcess); UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), UTIL_SEQ_RFU, SendTxData); /* Init Info table used by LmHandler*/ LoraInfo_Init(); /* Init the Lora Stack*/ LmHandlerInit(&LmHandlerCallbacks); LmHandlerConfigure(&LmHandlerParams); /* USER CODE BEGIN LoRaWAN_Init_2 */ UTIL_TIMER_Start(&JoinLedTimer); /* USER CODE END LoRaWAN_Init_2 */ LmHandlerJoin(ActivationType); if (EventType == TX_ON_TIMER) { /* send every time timer elapses */ UTIL_TIMER_Create(&TxTimer, 0xFFFFFFFFU, UTIL_TIMER_PERIODIC, OnTxTimerEvent, NULL); UTIL_TIMER_SetPeriod(&TxTimer, APP_TX_DUTYCYCLE); UTIL_TIMER_Start(&TxTimer); } else { /* USER CODE BEGIN LoRaWAN_Init_3 */ /* send every time button is pushed */ //BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI); /* 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 USER_BUTTON_PIN: UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0); 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 */ if ((appData != NULL) || (params != NULL)) { LED_On(LED_GPIO_PORT,LED_BLUE); UTIL_TIMER_Start(&RxLedTimer); static const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" }; APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### ========== MCPS-Indication ==========\r\n"); APP_LOG(TS_OFF, VLEVEL_H, "###### D/L FRAME:%04d | SLOT:%s | PORT:%d | DR:%d | RSSI:%d | SNR:%d\r\n", params->DownlinkCounter, slotStrings[params->RxSlot], appData->Port, params->Datarate, params->Rssi, params->Snr); 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"); LED_Off(LED_GPIO_PORT,LED_RED1); } else { APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n"); LED_On(LED_GPIO_PORT,LED_RED1); } } break; default: break; } } /* USER CODE END OnRxData_1 */ } static void SendTxData(void) { /* USER CODE BEGIN SendTxData_1 */ uint16_t pressure = 0; int16_t temperature = 0; int16_t light = 0;//@Murata add a light byte in packet sensor_t sensor_data; UTIL_TIMER_Time_t nextTxIn = 0; #ifdef CAYENNE_LPP uint8_t channel = 0; #else 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); #if defined (SENSOR_ENABLED) && (SENSOR_ENABLED == 1) temperature = (int16_t) sensor_data.temperature; #else temperature = (SYS_GetTemperatureLevel() >> 8); light = GetLightLevel();//@Murata add light byte in packet #endif /* SENSOR_ENABLED */ pressure = (uint16_t)(sensor_data.pressure * 100 / 10); /* in hPa / 10 */ AppData.Port = LORAWAN_USER_APP_PORT; #ifdef CAYENNE_LPP CayenneLppReset(); CayenneLppAddBarometricPressure(channel++, pressure); CayenneLppAddTemperature(channel++, 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 */ 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); AppData.Buffer[i++] = (uint8_t)((light >> 8) & 0xFF); AppData.Buffer[i++] = (uint8_t)(light & 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.BufferSize = i; #endif /* CAYENNE_LPP */ /*---------------Added by Murata for AS923-2 ------------------------*/ #if 1 //Verify 'Datarate >= User default (minimum) Datarate' MibRequestConfirm_t mibreq; mibreq.Type = MIB_CHANNELS_DATARATE; if (LoRaMacMibGetRequestConfirm(&mibreq) != LORAMAC_STATUS_OK) { APP_LOG(TS_ON, VLEVEL_L, "LORAMAC_HANDLER_ERROR\r\n"); } if (mibreq.Param.ChannelsDatarate < LORAWAN_DEFAULT_DATA_RATE) { mibreq.Param.ChannelsDatarate = LORAWAN_DEFAULT_DATA_RATE; // if the current DR < minimum User DR, set it at the minimum DR. if (LoRaMacMibSetRequestConfirm(&mibreq) != LORAMAC_STATUS_OK) { APP_LOG(TS_ON, VLEVEL_L, "LORAMAC_HANDLER_ERROR\r\n"); } } #endif /*---------------Added by Murata for AS923-2 END--------------------*/ if (LORAMAC_HANDLER_SUCCESS == LmHandlerSend(&AppData, LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, &nextTxIn, false)) { APP_LOG(TS_ON, VLEVEL_L, "SEND REQUEST\r\n"); } else if (nextTxIn > 0) { APP_LOG(TS_ON, VLEVEL_L, "Next Tx in : ~%d second(s)\r\n", (nextTxIn / 1000)); } /* 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) { LED_Off(LED_GPIO_PORT,LED_RED2); } static void OnRxTimerLedEvent(void *context) { LED_Off(LED_GPIO_PORT,LED_BLUE) ; } static void OnJoinTimerLedEvent(void *context) { LED_Toggle(LED_GPIO_PORT,LED_RED1) ; } /* 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) { LED_On(LED_GPIO_PORT,LED_RED2) ; 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); LED_Off(LED_GPIO_PORT,LED_RED1) ; 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"); } } /* USER CODE END OnJoinRequest_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 */ } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/