/* 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" #ifdef CLOCK_SYNC #include "LmhpClockSync.h" #endif #ifdef RC #include "ev1527.h" volatile uint8_t sts_rf_cloud_cmd=0; volatile uint8_t sts_prev_rf_cmd_pic, sts_rf_cmd_pic, prev_sts_rf_cmd_status; int sts_wet_humidity_threshold=50; #endif /* USER CODE BEGIN Includes */ #ifdef STS_WS #include "sts_weight_scale.h" #endif /* 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 ------------------------------------------------------------*/ /** * 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 -----------------------------------------------*/ static void SendRemoteControlData(void); /** * @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); static void OnSelfMeasureEnvironmentEvent(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; static UTIL_TIMER_Object_t MeaureEnvionmentTimer; /** * @brief Timer to handle the application Join Led to toggle */ static UTIL_TIMER_Object_t JoinLedTimer; /* USER CODE END PV */ static volatile bool IsClockSynched = false; /* Exported functions ---------------------------------------------------------*/ /* USER CODE BEGIN EF */ uint32_t STS_MeasureEnvironmentTime_sec=120; // in seconds volatile uint8_t sts_rf_cmd_status = 0; volatile uint8_t sts_rf_cmd_value = 0; //volatile uint8_t sts_rc_node_cmd = 0; volatile bool sts_rf_projector_power_on = false; /* 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)); UTIL_TIMER_Create(&TxLedTimer, LED_PERIOD_TIME, UTIL_TIMER_ONESHOT, OnTxTimerLedEvent, NULL); UTIL_TIMER_Create(&RxLedTimer, LED_PERIOD_TIME, UTIL_TIMER_ONESHOT, OnRxTimerLedEvent, NULL); UTIL_TIMER_Create(&JoinLedTimer, LED_PERIOD_TIME, UTIL_TIMER_PERIODIC, OnJoinTimerLedEvent, NULL); UTIL_TIMER_Create(&MeaureEnvionmentTimer, 1000*STS_MeasureEnvironmentTime_sec, UTIL_TIMER_PERIODIC, OnSelfMeasureEnvironmentEvent, 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); UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_STS_RemoteControlButtonEvent), UTIL_SEQ_RFU, SendRemoteControlData); // TODO XXX 2024-06-04 #ifdef CLOCK_SYNC LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); IsClockSynched = false; #endif /* Init Info table used by LmHandler*/ LoraInfo_Init(); /* Init the Lora Stack*/ LmHandlerInit(&LmHandlerCallbacks, APP_VERSION); LmHandlerConfigure(&LmHandlerParams); /* USER CODE BEGIN LoRaWAN_Init_2 */ UTIL_TIMER_Start(&JoinLedTimer); /* USER CODE END LoRaWAN_Init_2 */ LmHandlerJoin(ActivationType, ForceRejoin); if (EventType == TX_ON_TIMER) { /* send every time timer elapses */ UTIL_TIMER_Create(&TxTimer, TxPeriodicity, UTIL_TIMER_ONESHOT, OnTxTimerEvent, NULL); UTIL_TIMER_Start(&TxTimer); } else { /* USER CODE BEGIN LoRaWAN_Init_3 */ /* USER CODE END LoRaWAN_Init_3 */ } /* USER CODE BEGIN LoRaWAN_Init_Last */ UTIL_TIMER_Start(&MeaureEnvionmentTimer); /* USER CODE END LoRaWAN_Init_Last */ } /* USER CODE BEGIN PB_Callbacks */ #if 0 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; } } #endif /* 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; } } 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; 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 */ } #define STS_RF_CMD_NONE 0 #define STS_NODESELF_CMD_PENDING_SEND 1 #define STS_CLOUD_CMD_PENDING_SEND 2 #define STS_RF_CMD_SENT_OUT 3 #define RF_PROECTOR_POWER_OFF 1 #define RF_PROECTOR_POWER_ON 2 typedef struct service_period_per_day { uint8_t enable; // enable this time slot or not uint8_t start_hour; // start hour 00--23 in 24hour uint8_t start_minute; // start minute 00--59 uint8_t end_hour; // end hour 00-23 in 24 hour uint8_t end_minute; // end minute 00--60 } t_service_period_per_day; t_service_period_per_day rc_service_period[4]={ {1,6,30,12,00}, {1,13,00, 18,00}, {0,0,0,0,0}, {0,0,0,0,0} }; static bool sts_rc_in_service_period=true; static void STS_Check_Service_Period(void) { struct tm time_now={0}; SysTime_t UnixEpoch = SysTimeGet(); UnixEpoch.Seconds -= 18; /*removing leap seconds*/ SysTimeLocalTime(UnixEpoch.Seconds, &time_now); bool sts_rc_in_service_period_try = false; for (uint8_t i=0; i<4; i++) { if (rc_service_period[i].enable) { if ((time_now.tm_hour >= rc_service_period[i].start_hour) && (time_now.tm_hour < rc_service_period[i].end_hour)) { sts_rc_in_service_period_try = true; } } } sts_rc_in_service_period = sts_rc_in_service_period_try; } extern uint8_t rf_payload[3]; /* * measure humidity --> over threshold --> caution wet floor + power on() * measure humidity --> not over threshold --> welcome pic or power off * button- switch 1:power off ---> power off * button- switch 2/1: local control ? via internal sensor * button- switch 2/2: cloud control ? * key_name --> 1/2/3/4/5/6/7/8 or ON/OFF/FIRST/NEXT/5S/10S/15S/30S * cmd_value---> key2cmd[key_name] 0x01,0x08,0x0c,0x * */ static void SendRemoteControlData(void) { /* sts_rf_cmd_status: global cmd process status */ /* */ switch (sts_rf_cmd_status) { case STS_NODESELF_CMD_PENDING_SEND: if (sts_rf_projector_power_on == true) //rf_payload[2] |= rf_button_2_cmd4bits[sts_rc_cmd_value]; STS_RF_Send_Button_Multi_Times(rf_payload, sts_rf_cmd_value); sts_rf_cmd_status = STS_RF_CMD_SENT_OUT; break; case STS_CLOUD_CMD_PENDING_SEND: // CLOUD DOWNLINK INSTRUCTION if (sts_rf_cloud_cmd == BUTTON_OFF) // if ask to power off { sts_rf_cmd_value = BUTTON_OFF; //rf_payload[2] |= rf_button_2_cmd4bits[sts_rc_cmd_value]; //rf_cmd[BUTTON_ON];//sts_rf_cloud_cmd; sts_rf_projector_power_on = false; } else { // else other show pic cmd if (sts_rf_projector_power_on == false) // if it's still powered off yet, power it on { sts_rf_cmd_value = BUTTON_ON; //rf_payload[2] |= rf_button_2_cmd4bits[sts_rc_cmd_value]; STS_RF_Send_Button_Multi_Times(rf_payload, sts_rf_cloud_cmd); sts_rf_projector_power_on = true; HAL_Delay(2000); // need to test 2 sec good enough or not... maybe 30 seconds... } // delay 2 sec, then send cloud cmd... //rf_payload[2] |= rf_button_2_cmd4bits[sts_rc_cloud_cmd]; STS_RF_Send_Button_Multi_Times(rf_payload, sts_rf_cloud_cmd); } sts_rf_cmd_status = STS_RF_CMD_SENT_OUT; break; case STS_RF_CMD_SENT_OUT: // Do nothing break; case STS_RF_CMD_NONE: // No cmd to send out break; case 4: break; case 5: break; case 6: break; case 7: break; case 8: break; case 9: break; case 10: break; case 11: break; case 12: break; case 13: break; case 14: break; case 15: break; } } static void SendTxData(void) { /* USER CODE BEGIN SendTxData_1 */ LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; uint8_t batteryLevel = GetBatteryLevel(); sensor_t sensor_data; #ifdef STS_WS sts_sensor_t sts_sensor_data; #endif 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); #ifdef STS_WS sts_weight_scale(&sts_sensor_data); APP_LOG(TS_ON, VLEVEL_M, "Weight Scale : %d\r\n", sts_sensor_data.weight_scale_value); #endif 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); } #ifdef STS_WS AppData.Buffer[i++] = (uint8_t)(sts_sensor_data.weight_scale_value >> 8& 0xFF); AppData.Buffer[i++] = (uint8_t)(sts_sensor_data.weight_scale_value & 0xFF); #endif AppData.BufferSize = i; #endif /* CAYENNE_LPP */ #ifdef CLOCK_SYNC if( IsClockSynched == false ) { status = LmhpClockSyncAppTimeReq( ); if (LORAMAC_HANDLER_SUCCESS == status) { OnSysTimeUpdate(); } } #endif if ((JoinLedTimer.IsRunning) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET)) { UTIL_TIMER_Stop(&JoinLedTimer); 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 */ 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 */ } #define STS_RF_PROJECTOR_PENDING_SEND 1U #define STS_RF_PROJECTOR_POWER_OFF 2 #define STS_RF_SHOW_PIC_DARK 0 #define STS_RF_SHOW_PIC_WET_FLOOR 4U #define STS_RF_SHOW_PIC_WELCOME 5 #define STS_RF_PROJECTOR_KEEP_STATE 0 void static sts_rc_cmd_rf(uint8_t pic_id) { switch (pic_id) { case 1: break; case 2: break; case 3: break; case 4: break; } if (sts_prev_rf_cmd_pic != STS_RF_SHOW_PIC_WET_FLOOR) { sts_rf_cmd_status = STS_RF_PROJECTOR_PENDING_SEND; sts_rf_cmd_pic = STS_RF_SHOW_PIC_WET_FLOOR; } else { sts_rf_cmd_status = STS_RF_PROJECTOR_KEEP_STATE; if (prev_sts_rf_cmd_status != STS_RF_PROJECTOR_POWER_OFF) { sts_rf_cmd_pic = STS_RF_SHOW_PIC_WELCOME; // or other messages, rotate other pictures sts_rf_cmd_status = STS_RF_PROJECTOR_KEEP_STATE; } } sts_prev_rf_cmd_pic = STS_RF_SHOW_PIC_WET_FLOOR; } static void OnSelfMeasureEnvironmentEvent(void *context) { sensor_t sensor_data; EnvSensors_Read(&sensor_data); STS_Check_Service_Period(); if (sts_rc_in_service_period == true) { if ((sensor_data.humidity >= sts_wet_humidity_threshold) ) // wet floor condition { APP_LOG(TS_OFF, VLEVEL_M, "\r\n*** Humidity=%2d (%), over threshold %2d (%), start WET FLOOR PICTURE \r\n", sensor_data.humidity, sts_wet_humidity_threshold); sts_rf_cmd_pic |= STS_RF_SHOW_PIC_WET_FLOOR; sts_rc_cmd_rf(sts_rf_cmd_pic); } else { // non wet floor situation, so just display welcome message or other messages, instead of wet floor APP_LOG(TS_OFF, VLEVEL_M, "\r\n*** Humidity=%2d (%), less than threshold %2d (%), Power Off projector\r\n", sensor_data.humidity, sts_wet_humidity_threshold); sts_rf_cmd_pic &= ~STS_RF_SHOW_PIC_WET_FLOOR; sts_rc_cmd_rf(sts_rf_cmd_pic); } UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_STS_RemoteControlButtonEvent), CFG_SEQ_Prio_0); } else { // not in service period, just power off projector in case it's still on if (sts_rf_cmd_status != STS_RF_PROJECTOR_POWER_OFF) { sts_rf_cmd_status = STS_RF_PROJECTOR_POWER_OFF; sts_rf_cmd_pic = STS_RF_SHOW_PIC_DARK; UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_STS_RemoteControlButtonEvent), CFG_SEQ_Prio_0); } else { // already power off, do nothing just return return; } } } 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 */ IsClockSynched = true; /* 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 */ }