/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file subghz_phy_app.c * @author MCD Application Team * @brief Application of the SubGHz_Phy 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 "subghz_phy_app.h" #include "radio.h" /* USER CODE BEGIN Includes */ #include "stm32_timer.h" #include "stm32_seq.h" #include "utilities_def.h" #include "app_version.h" #include "subghz_phy_version.h" /* USER CODE END Includes */ /* External variables ---------------------------------------------------------*/ /* USER CODE BEGIN EV */ /* USER CODE END EV */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ typedef enum { RX, RX_TIMEOUT, RX_ERROR, TX, TX_TIMEOUT, } States_t; /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* Configurations */ /*Timeout*/ #define RX_TIMEOUT_VALUE 3000 #define TX_TIMEOUT_VALUE 3000 /* PING string*/ #define PING "PING" /* PONG string*/ #define PONG "PONG" /*Size of the payload to be sent*/ /* Size must be greater of equal the PING and PONG*/ #define MAX_APP_BUFFER_SIZE 255 #if (PAYLOAD_LEN > MAX_APP_BUFFER_SIZE) #error PAYLOAD_LEN must be less or equal than MAX_APP_BUFFER_SIZE #endif /* (PAYLOAD_LEN > MAX_APP_BUFFER_SIZE) */ /* wait for remote to be in Rx, before sending a Tx frame*/ #define RX_TIME_MARGIN 200 /* Afc bandwidth in Hz */ #define FSK_AFC_BANDWIDTH 83333 /* LED blink Period*/ #define LED_PERIOD_MS 200 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* Radio events function pointer */ static RadioEvents_t RadioEvents; /* USER CODE BEGIN PV */ /*Ping Pong FSM states */ static States_t State = RX; /* App Rx Buffer*/ static uint8_t BufferRx[MAX_APP_BUFFER_SIZE]; /* App Tx Buffer*/ static uint8_t BufferTx[MAX_APP_BUFFER_SIZE]; /* Last Received Buffer Size*/ uint16_t RxBufferSize = 0; /* Last Received packer Rssi*/ int8_t RssiValue = 0; /* Last Received packer SNR (in Lora modulation)*/ int8_t SnrValue = 0; /* Led Timers objects*/ static UTIL_TIMER_Object_t timerLed; /* device state. Master: true, Slave: false*/ bool isMaster = true; /* random delay to make sure 2 devices will sync*/ /* the closest the random delays are, the longer it will take for the devices to sync when started simultaneously*/ static int32_t random_delay; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ /*! * @brief Function to be executed on Radio Tx Done event */ static void OnTxDone(void); /** * @brief Function to be executed on Radio Rx Done event * @param payload ptr of buffer received * @param size buffer size * @param rssi * @param LoraSnr_FskCfo */ static void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo); /** * @brief Function executed on Radio Tx Timeout event */ static void OnTxTimeout(void); /** * @brief Function executed on Radio Rx Timeout event */ static void OnRxTimeout(void); /** * @brief Function executed on Radio Rx Error event */ static void OnRxError(void); /* USER CODE BEGIN PFP */ /** * @brief Function executed on when led timer elapses * @param context ptr of LED context */ static void OnledEvent(void *context); /** * @brief PingPong state machine implementation */ static void PingPong_Process(void); /* USER CODE END PFP */ /* Exported functions ---------------------------------------------------------*/ void SubghzApp_Init(void) { /* USER CODE BEGIN SubghzApp_Init_1 */ APP_LOG(TS_OFF, VLEVEL_M, "\n\rPING PONG\n\r"); /* Get SubGHY_Phy 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 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)); /* Led Timers*/ UTIL_TIMER_Create(&timerLed, LED_PERIOD_MS, UTIL_TIMER_ONESHOT, OnledEvent, NULL); UTIL_TIMER_Start(&timerLed); /* USER CODE END SubghzApp_Init_1 */ /* Radio initialization */ RadioEvents.TxDone = OnTxDone; RadioEvents.RxDone = OnRxDone; RadioEvents.TxTimeout = OnTxTimeout; RadioEvents.RxTimeout = OnRxTimeout; RadioEvents.RxError = OnRxError; Radio.Init(&RadioEvents); /* USER CODE BEGIN SubghzApp_Init_2 */ /*calculate random delay for synchronization*/ random_delay = (Radio.Random()) >> 22; /*10bits random e.g. from 0 to 1023 ms*/ /* Radio Set frequency */ Radio.SetChannel(RF_FREQUENCY); /* Radio configuration */ #if ((USE_MODEM_LORA == 1) && (USE_MODEM_FSK == 0)) APP_LOG(TS_OFF, VLEVEL_M, "---------------\n\r"); APP_LOG(TS_OFF, VLEVEL_M, "LORA_MODULATION\n\r"); APP_LOG(TS_OFF, VLEVEL_M, "LORA_BW=%d kHz\n\r", (1 << LORA_BANDWIDTH) * 125); APP_LOG(TS_OFF, VLEVEL_M, "LORA_SF=%d\n\r", LORA_SPREADING_FACTOR); Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE); Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, true, 0, 0, LORA_IQ_INVERSION_ON, true); Radio.SetMaxPayloadLength(MODEM_LORA, MAX_APP_BUFFER_SIZE); #elif ((USE_MODEM_LORA == 0) && (USE_MODEM_FSK == 1)) APP_LOG(TS_OFF, VLEVEL_M, "---------------\n\r"); APP_LOG(TS_OFF, VLEVEL_M, "FSK_MODULATION\n\r"); APP_LOG(TS_OFF, VLEVEL_M, "FSK_BW=%d Hz\n\r", FSK_BANDWIDTH); APP_LOG(TS_OFF, VLEVEL_M, "FSK_DR=%d bits/s\n\r", FSK_DATARATE); Radio.SetTxConfig(MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, FSK_DATARATE, 0, FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, true, 0, 0, 0, TX_TIMEOUT_VALUE); Radio.SetRxConfig(MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, 0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, 0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, 0, 0, false, true); Radio.SetMaxPayloadLength(MODEM_FSK, MAX_APP_BUFFER_SIZE); #else #error "Please define a modulation in the subghz_phy_app.h file." #endif /* USE_MODEM_LORA | USE_MODEM_FSK */ /*fills tx buffer*/ memset(BufferTx, 0x0, MAX_APP_BUFFER_SIZE); APP_LOG(TS_ON, VLEVEL_L, "rand=%d\n\r", random_delay); /*starts reception*/ Radio.Rx(RX_TIMEOUT_VALUE + random_delay); /*register task to to be run in while(1) after Radio IT*/ UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), UTIL_SEQ_RFU, PingPong_Process); /* USER CODE END SubghzApp_Init_2 */ } /* USER CODE BEGIN EF */ /* USER CODE END EF */ /* Private functions ---------------------------------------------------------*/ static void OnTxDone(void) { /* USER CODE BEGIN OnTxDone */ APP_LOG(TS_ON, VLEVEL_L, "OnTxDone\n\r"); /* Update the State of the FSM*/ State = TX; /* Run PingPong process in background*/ UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0); /* USER CODE END OnTxDone */ } static void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo) { /* USER CODE BEGIN OnRxDone */ APP_LOG(TS_ON, VLEVEL_L, "OnRxDone\n\r"); #if ((USE_MODEM_LORA == 1) && (USE_MODEM_FSK == 0)) APP_LOG(TS_ON, VLEVEL_L, "RssiValue=%d dBm, SnrValue=%ddB\n\r", rssi, LoraSnr_FskCfo); /* Record payload Signal to noise ratio in Lora*/ SnrValue = LoraSnr_FskCfo; #endif /* USE_MODEM_LORA | USE_MODEM_FSK */ #if ((USE_MODEM_LORA == 0) && (USE_MODEM_FSK == 1)) APP_LOG(TS_ON, VLEVEL_L, "RssiValue=%d dBm, Cfo=%dkHz\n\r", rssi, LoraSnr_FskCfo); SnrValue = 0; /*not applicable in GFSK*/ #endif /* USE_MODEM_LORA | USE_MODEM_FSK */ /* Update the State of the FSM*/ State = RX; /* Clear BufferRx*/ memset(BufferRx, 0, MAX_APP_BUFFER_SIZE); /* Record payload size*/ RxBufferSize = size; if (RxBufferSize <= MAX_APP_BUFFER_SIZE) { memcpy(BufferRx, payload, RxBufferSize); } /* Record Received Signal Strength*/ RssiValue = rssi; /* Record payload content*/ APP_LOG(TS_ON, VLEVEL_H, "payload. size=%d \n\r", size); for (int32_t i = 0; i < PAYLOAD_LEN; i++) { APP_LOG(TS_OFF, VLEVEL_H, "%02X", BufferRx[i]); if (i % 16 == 15) { APP_LOG(TS_OFF, VLEVEL_H, "\n\r"); } } APP_LOG(TS_OFF, VLEVEL_H, "\n\r"); /* Run PingPong process in background*/ UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0); /* USER CODE END OnRxDone */ } static void OnTxTimeout(void) { /* USER CODE BEGIN OnTxTimeout */ APP_LOG(TS_ON, VLEVEL_L, "OnTxTimeout\n\r"); /* Update the State of the FSM*/ State = TX_TIMEOUT; /* Run PingPong process in background*/ UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0); /* USER CODE END OnTxTimeout */ } static void OnRxTimeout(void) { /* USER CODE BEGIN OnRxTimeout */ APP_LOG(TS_ON, VLEVEL_L, "OnRxTimeout\n\r"); /* Update the State of the FSM*/ State = RX_TIMEOUT; /* Run PingPong process in background*/ UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0); /* USER CODE END OnRxTimeout */ } static void OnRxError(void) { /* USER CODE BEGIN OnRxError */ APP_LOG(TS_ON, VLEVEL_L, "OnRxError\n\r"); /* Update the State of the FSM*/ State = RX_ERROR; /* Run PingPong process in background*/ UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0); /* USER CODE END OnRxError */ } /* USER CODE BEGIN PrFD */ static void PingPong_Process(void) { Radio.Sleep(); switch (State) { case RX: if (isMaster == true) { if (RxBufferSize > 0) { if (strncmp((const char *)BufferRx, PONG, sizeof(PONG) - 1) == 0) { UTIL_TIMER_Stop(&timerLed); /* switch off green led */ HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); /* LED_GREEN */ /* master toggles red led */ HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); /* LED_RED */ /* Add delay between RX and TX */ HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN); /* master sends PING*/ APP_LOG(TS_ON, VLEVEL_L, "..." "PING" "\n\r"); APP_LOG(TS_ON, VLEVEL_L, "Master Tx start\n\r"); memcpy(BufferTx, PING, sizeof(PING) - 1); Radio.Send(BufferTx, PAYLOAD_LEN); } else if (strncmp((const char *)BufferRx, PING, sizeof(PING) - 1) == 0) { /* A master already exists then become a slave */ isMaster = false; APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); } else /* valid reception but neither a PING or a PONG message */ { /* Set device as master and start again */ isMaster = true; APP_LOG(TS_ON, VLEVEL_L, "Master Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); } } } else { if (RxBufferSize > 0) { if (strncmp((const char *)BufferRx, PING, sizeof(PING) - 1) == 0) { UTIL_TIMER_Stop(&timerLed); /* switch off red led */ HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */ /* slave toggles green led */ HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); /* LED_GREEN */ /* Add delay between RX and TX */ HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN); /*slave sends PONG*/ APP_LOG(TS_ON, VLEVEL_L, "..." "PONG" "\n\r"); APP_LOG(TS_ON, VLEVEL_L, "Slave Tx start\n\r"); memcpy(BufferTx, PONG, sizeof(PONG) - 1); Radio.Send(BufferTx, PAYLOAD_LEN); } else /* valid reception but not a PING as expected */ { /* Set device as master and start again */ isMaster = true; APP_LOG(TS_ON, VLEVEL_L, "Master Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); } } } break; case TX: APP_LOG(TS_ON, VLEVEL_L, "Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); break; case RX_TIMEOUT: case RX_ERROR: if (isMaster == true) { /* Send the next PING frame */ /* Add delay between RX and TX*/ /* add random_delay to force sync between boards after some trials*/ HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN + random_delay); APP_LOG(TS_ON, VLEVEL_L, "Master Tx start\n\r"); /* master sends PING*/ memcpy(BufferTx, PING, sizeof(PING) - 1); Radio.Send(BufferTx, PAYLOAD_LEN); } else { APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); } break; case TX_TIMEOUT: APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r"); Radio.Rx(RX_TIMEOUT_VALUE); break; default: break; } } static void OnledEvent(void *context) { HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); /* LED_GREEN */ HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); /* LED_RED */ UTIL_TIMER_Start(&timerLed); } /* USER CODE END PrFD */