/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file App/app_ble.c * @author MCD Application Team * @brief BLE Application ***************************************************************************** * @attention * * Copyright (c) 2026 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 "main.h" #include "app_common.h" #include "dbg_trace.h" #include "ble.h" #include "tl.h" #include "app_ble.h" #include "stm32_seq.h" #include "shci.h" #include "stm32_lpm.h" #include "otp.h" #include "eddystone_beacon.h" #include "eddystone_uid_service.h" #include "eddystone_url_service.h" #include "eddystone_tlm_service.h" #include "ibeacon_service.h" #include "ibeacon.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private defines -----------------------------------------------------------*/ #define APPBLE_GAP_DEVICE_NAME_LENGTH 7 /** * Boot Mode: 1 (OTA) * Sector Index: 6 * Nb Sectors : 1 */ #define BOOT_MODE_AND_SECTOR 0x010601 #define APP_SECTORS 7 #define DATA_SECTOR 6 #define BD_ADDR_SIZE_LOCAL 6 #define BLE_DEFAULT_PIN (111111) /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; static const uint8_t a_MBdAddr[BD_ADDR_SIZE_LOCAL] = { (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)), (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8), (uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16), (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24), (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32), (uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40) }; static uint8_t a_BdAddrUdn[BD_ADDR_SIZE_LOCAL]; /** * Identity root key used to derive IRK and DHK(Legacy) */ static const uint8_t a_BLE_CfgIrValue[16] = CFG_BLE_IR; /** * Encryption root key used to derive LTK(Legacy) and CSRK */ static const uint8_t a_BLE_CfgErValue[16] = CFG_BLE_ER; static uint8_t sector_type; /** * These are the two tags used to manage a power failure during OTA * The MagicKeywordAdress shall be mapped @0x140 from start of the binary image * The MagicKeywordvalue is checked in the ble_ota application */ PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ; PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ static void BLE_UserEvtRx(void *p_Payload); static void BLE_StatusNot(HCI_TL_CmdStatus_t Status); static void Ble_Tl_Init(void); static void Ble_Hci_Gap_Gatt_Init(void); static const uint8_t* BleGetBdAddress(void); static void Beacon_Update(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* External variables --------------------------------------------------------*/ /* USER CODE BEGIN EV */ /* USER CODE END EV */ /* Functions Definition ------------------------------------------------------*/ void APP_BLE_Init(void) { SHCI_CmdStatus_t status; /* USER CODE BEGIN APP_BLE_Init_1 */ /* USER CODE END APP_BLE_Init_1 */ SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { {{0,0,0}}, /**< Header unused */ {0, /** pBleBufferAddress not used */ 0, /** BleBufferSize not used */ CFG_BLE_NUM_GATT_ATTRIBUTES, CFG_BLE_NUM_GATT_SERVICES, CFG_BLE_ATT_VALUE_ARRAY_SIZE, CFG_BLE_NUM_LINK, CFG_BLE_DATA_LENGTH_EXTENSION, CFG_BLE_PREPARE_WRITE_LIST_SIZE, CFG_BLE_MBLOCK_COUNT, CFG_BLE_MAX_ATT_MTU, CFG_BLE_PERIPHERAL_SCA, CFG_BLE_CENTRAL_SCA, CFG_BLE_LS_SOURCE, CFG_BLE_MAX_CONN_EVENT_LENGTH, CFG_BLE_HSE_STARTUP_TIME, CFG_BLE_VITERBI_MODE, CFG_BLE_OPTIONS, 0, CFG_BLE_MAX_COC_INITIATOR_NBR, CFG_BLE_MIN_TX_POWER, CFG_BLE_MAX_TX_POWER, CFG_BLE_RX_MODEL_CONFIG, CFG_BLE_MAX_ADV_SET_NBR, CFG_BLE_MAX_ADV_DATA_LEN, CFG_BLE_TX_PATH_COMPENS, CFG_BLE_RX_PATH_COMPENS, CFG_BLE_CORE_VERSION, CFG_BLE_OPTIONS_EXT, CFG_BLE_MAX_ADD_EATT_BEARERS } }; /** * Initialize Ble Transport Layer */ Ble_Tl_Init(); #if (CFG_LPM_STANDBY_SUPPORTED == 0) UTIL_LPM_SetOffMode(1U << CFG_LPM_APP_BLE, UTIL_LPM_DISABLE); #endif /* CFG_LPM_STANDBY_SUPPORTED == 0 */ /** * Register the hci transport layer to handle BLE User Asynchronous Events */ UTIL_SEQ_RegTask(1<data; switch (p_event_pckt->evt) { case HCI_DISCONNECTION_COMPLETE_EVT_CODE: /* USER CODE BEGIN EVT_DISCONN_COMPLETE */ /* USER CODE END EVT_DISCONN_COMPLETE */ break; /* HCI_DISCONNECTION_COMPLETE_EVT_CODE */ case HCI_LE_META_EVT_CODE: p_meta_evt = (evt_le_meta_event*) p_event_pckt->data; /* USER CODE BEGIN EVT_LE_META_EVENT */ /* USER CODE END EVT_LE_META_EVENT */ switch (p_meta_evt->subevent) { case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: /* USER CODE BEGIN SUBEVENT_DEFAULT */ /* USER CODE END SUBEVENT_DEFAULT */ break; default: /* USER CODE BEGIN SUBEVENT_DEFAULT */ /* USER CODE END SUBEVENT_DEFAULT */ break; } /* USER CODE BEGIN META_EVT */ /* USER CODE END META_EVT */ break; /* HCI_LE_META_EVT_CODE */ case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: p_blecore_evt = (evt_blecore_aci*) p_event_pckt->data; /* USER CODE BEGIN EVT_VENDOR */ /* USER CODE END EVT_VENDOR */ switch (p_blecore_evt->ecode) { /* USER CODE BEGIN ecode */ /* USER CODE END ecode */ case ACI_GAP_PROC_COMPLETE_VSEVT_CODE: /* USER CODE BEGIN EVT_BLUE_GAP_PROCEDURE_COMPLETE */ /* USER CODE END EVT_BLUE_GAP_PROCEDURE_COMPLETE */ break; /* ACI_GAP_PROC_COMPLETE_VSEVT_CODE */ /* USER CODE BEGIN BLUE_EVT */ /* USER CODE END BLUE_EVT */ } break; /* HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE */ /* USER CODE BEGIN EVENT_PCKT */ /* USER CODE END EVENT_PCKT */ default: /* USER CODE BEGIN ECODE_DEFAULT*/ /* USER CODE END ECODE_DEFAULT*/ break; } return (SVCCTL_UserEvtFlowEnable); } /* USER CODE BEGIN FD*/ /* USER CODE END FD*/ /************************************************************* * * LOCAL FUNCTIONS * *************************************************************/ static void Ble_Tl_Init(void) { HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; Hci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&BleCmdBuffer; Hci_Tl_Init_Conf.StatusNotCallBack = BLE_StatusNot; hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); return; } static void Ble_Hci_Gap_Gatt_Init(void) { uint8_t role; uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle; const uint8_t *p_bd_addr; uint16_t a_appearance[1] = {BLE_CFG_GAP_APPEARANCE}; tBleStatus ret = BLE_STATUS_INVALID_PARAMS; /* USER CODE BEGIN Ble_Hci_Gap_Gatt_Init*/ /* USER CODE END Ble_Hci_Gap_Gatt_Init*/ APP_DBG_MSG("==>> Start Ble_Hci_Gap_Gatt_Init function\n"); /** * Initialize HCI layer */ /*HCI Reset to synchronise BLE Stack*/ ret = hci_reset(); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : hci_reset command, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: hci_reset command\n"); } /** * Write the BD Address */ p_bd_addr = BleGetBdAddress(); ret = aci_hal_write_config_data(CONFIG_DATA_PUBLIC_ADDRESS_OFFSET, CONFIG_DATA_PUBLIC_ADDRESS_LEN, (uint8_t*) p_bd_addr); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_hal_write_config_data command - CONFIG_DATA_PUBLIC_ADDRESS_OFFSET, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_hal_write_config_data command - CONFIG_DATA_PUBLIC_ADDRESS_OFFSET\n"); APP_DBG_MSG(" Public Bluetooth Address: %02x:%02x:%02x:%02x:%02x:%02x\n",p_bd_addr[5],p_bd_addr[4],p_bd_addr[3],p_bd_addr[2],p_bd_addr[1],p_bd_addr[0]); } /** * Write Identity root key used to derive IRK and DHK(Legacy) */ ret = aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)a_BLE_CfgIrValue); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_hal_write_config_data command - CONFIG_DATA_IR_OFFSET, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_hal_write_config_data command - CONFIG_DATA_IR_OFFSET\n"); } /** * Write Encryption root key used to derive LTK and CSRK */ ret = aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)a_BLE_CfgErValue); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_hal_write_config_data command - CONFIG_DATA_ER_OFFSET, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_hal_write_config_data command - CONFIG_DATA_ER_OFFSET\n"); } /** * Set TX Power. */ ret = aci_hal_set_tx_power_level(1, CFG_TX_POWER); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_hal_set_tx_power_level command, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_hal_set_tx_power_level command\n"); } /** * Initialize GATT interface */ ret = aci_gatt_init(); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_gatt_init command, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_gatt_init command\n"); } /** * Initialize GAP interface */ role = 0; #if (BLE_CFG_PERIPHERAL == 1) role |= GAP_PERIPHERAL_ROLE; #endif /* BLE_CFG_PERIPHERAL == 1 */ #if (BLE_CFG_CENTRAL == 1) role |= GAP_CENTRAL_ROLE; #endif /* BLE_CFG_CENTRAL == 1 */ /* USER CODE BEGIN Role_Mngt*/ /* USER CODE END Role_Mngt */ if (role > 0) { const char *name = "STM32WB"; ret = aci_gap_init(role, 0, APPBLE_GAP_DEVICE_NAME_LENGTH, &gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle); if (ret != BLE_STATUS_SUCCESS) { APP_DBG_MSG(" Fail : aci_gap_init command, result: 0x%x \n", ret); } else { APP_DBG_MSG(" Success: aci_gap_init command\n"); } ret = aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name); if (ret != BLE_STATUS_SUCCESS) { BLE_DBG_SVCCTL_MSG(" Fail : aci_gatt_update_char_value - Device Name\n"); } else { BLE_DBG_SVCCTL_MSG(" Success: aci_gatt_update_char_value - Device Name\n"); } } ret = aci_gatt_update_char_value(gap_service_handle, gap_appearance_char_handle, 0, 2, (uint8_t *)&a_appearance); if (ret != BLE_STATUS_SUCCESS) { BLE_DBG_SVCCTL_MSG(" Fail : aci_gatt_update_char_value - Appearance\n"); } else { BLE_DBG_SVCCTL_MSG(" Success: aci_gatt_update_char_value - Appearance\n"); } } static void Beacon_Update(void) { FLASH_EraseInitTypeDef erase; uint32_t pageError = 0; if (sector_type != 0) { erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.Page = sector_type; if (sector_type == APP_SECTORS) { erase.NbPages = 2; /* 2 sectors for beacon application */ } else { erase.NbPages = 1; /* 1 sector for beacon user data */ } HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_OPTVERR); HAL_FLASHEx_Erase(&erase, &pageError); HAL_FLASH_Lock(); } *(uint32_t*) SRAM1_BASE = BOOT_MODE_AND_SECTOR; /** * Boot Mode: 1 (OTA) * Sector Index: 6 * Nb Sectors : 1 */ NVIC_SystemReset(); } const uint8_t* BleGetBdAddress(void) { uint8_t *p_otp_addr; const uint8_t *p_bd_addr; uint32_t udn; uint32_t company_id; uint32_t device_id; udn = LL_FLASH_GetUDN(); if (udn != 0xFFFFFFFF) { company_id = LL_FLASH_GetSTCompanyID(); device_id = LL_FLASH_GetDeviceID(); /** * Public Address with the ST company ID * bit[47:24] : 24bits (OUI) equal to the company ID * bit[23:16] : Device ID. * bit[15:0] : The last 16bits from the UDN * Note: In order to use the Public Address in a final product, a dedicated * 24bits company ID (OUI) shall be bought. */ a_BdAddrUdn[0] = (uint8_t)(udn & 0x000000FF); a_BdAddrUdn[1] = (uint8_t)((udn & 0x0000FF00) >> 8); a_BdAddrUdn[2] = (uint8_t)device_id; a_BdAddrUdn[3] = (uint8_t)(company_id & 0x000000FF); a_BdAddrUdn[4] = (uint8_t)((company_id & 0x0000FF00) >> 8); a_BdAddrUdn[5] = (uint8_t)((company_id & 0x00FF0000) >> 16); p_bd_addr = (const uint8_t *)a_BdAddrUdn; } else { p_otp_addr = OTP_Read(0); if (p_otp_addr) { p_bd_addr = ((OTP_ID0_t*)p_otp_addr)->bd_address; } else { p_bd_addr = a_MBdAddr; } } return p_bd_addr; } /* USER CODE BEGIN FD_LOCAL_FUNCTION */ /* USER CODE END FD_LOCAL_FUNCTION */ /* USER CODE BEGIN FD_SPECIFIC_FUNCTIONS */ /* USER CODE END FD_SPECIFIC_FUNCTIONS */ /************************************************************* * * WRAP FUNCTIONS * *************************************************************/ void hci_notify_asynch_evt(void* p_Data) { UTIL_SEQ_SetTask(1 << CFG_TASK_HCI_ASYNCH_EVT_ID, CFG_SCH_PRIO_0); return; } void hci_cmd_resp_release(uint32_t Flag) { UTIL_SEQ_SetEvt(1 << CFG_IDLEEVT_HCI_CMD_EVT_RSP_ID); return; } void hci_cmd_resp_wait(uint32_t Timeout) { UTIL_SEQ_WaitEvt(1 << CFG_IDLEEVT_HCI_CMD_EVT_RSP_ID); return; } static void BLE_UserEvtRx(void *p_Payload) { SVCCTL_UserEvtFlowStatus_t svctl_return_status; tHCI_UserEvtRxParam *p_param; p_param = (tHCI_UserEvtRxParam *)p_Payload; svctl_return_status = SVCCTL_UserEvtRx((void *)&(p_param->pckt->evtserial)); if (svctl_return_status != SVCCTL_UserEvtFlowDisable) { p_param->status = HCI_TL_UserEventFlow_Enable; } else { p_param->status = HCI_TL_UserEventFlow_Disable; } return; } static void BLE_StatusNot(HCI_TL_CmdStatus_t Status) { uint32_t task_id_list; switch (Status) { case HCI_TL_CmdBusy: /** * All tasks that may send an aci/hci commands shall be listed here * This is to prevent a new command is sent while one is already pending */ task_id_list = (1 << CFG_LAST_TASK_ID_WITH_HCICMD) - 1; UTIL_SEQ_PauseTask(task_id_list); /* USER CODE BEGIN HCI_TL_CmdBusy */ /* USER CODE END HCI_TL_CmdBusy */ break; case HCI_TL_CmdAvailable: /** * All tasks that may send an aci/hci commands shall be listed here * This is to prevent a new command is sent while one is already pending */ task_id_list = (1 << CFG_LAST_TASK_ID_WITH_HCICMD) - 1; UTIL_SEQ_ResumeTask(task_id_list); /* USER CODE BEGIN HCI_TL_CmdAvailable */ /* USER CODE END HCI_TL_CmdAvailable */ break; default: /* USER CODE BEGIN Status */ /* USER CODE END Status */ break; } return; } void SVCCTL_ResumeUserEventFlow(void) { hci_resume_flow(); return; } /* USER CODE BEGIN FD_WRAP_FUNCTIONS */ /* USER CODE END FD_WRAP_FUNCTIONS */