/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file App/app_zigbee.c * @author MCD Application Team * @brief Zigbee Application. ****************************************************************************** * @attention * * Copyright (c) 2024 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 "app_common.h" #include "app_entry.h" #include "dbg_trace.h" #include "app_zigbee.h" #include "zigbee_interface.h" #include "shci.h" #include "stm_logging.h" #include "app_conf.h" #include "stm32wbxx_core_interface_def.h" #include "zigbee_types.h" #include "stm32_seq.h" /* Private includes -----------------------------------------------------------*/ #include #include "zcl/zcl.h" #include "zcl/general/zcl.commission.h" #include "zcl/general/zcl.onoff.h" /* USER CODE BEGIN Includes */ #include "ee.h" #include "hw_flash.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private defines -----------------------------------------------------------*/ #define APP_ZIGBEE_STARTUP_FAIL_DELAY 500U #define CHANNEL 11 #define ZED_SLEEP_TIME_30S 1 /* 30s sleep time unit */ #define SW1_ENDPOINT 1 /* Commissioning (endpoint 1) specific defines ------------------------------------------------*/ #define APS_SECURED_1 false #define COMMISSIONING_DEST_ENDPOINT_1 ZB_ENDPOINT_BCAST /* USER CODE BEGIN Commissioning (endpoint 1) defines */ /* USER CODE END Commissioning (endpoint 1) defines */ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macros ------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* External definition -------------------------------------------------------*/ enum ZbStatusCodeT ZbStartupWait(struct ZigBeeT *zb, struct ZbStartupT *config); /* USER CODE BEGIN ED */ /* USER CODE END ED */ /* Private function prototypes -----------------------------------------------*/ static void APP_ZIGBEE_StackLayersInit(void); static void APP_ZIGBEE_ConfigEndpoints(void); static void APP_ZIGBEE_NwkForm(void); static void APP_ZIGBEE_TraceError(const char *pMess, uint32_t ErrCode); static void APP_ZIGBEE_CheckWirelessFirmwareInfo(void); static void Wait_Getting_Ack_From_M0(void); static void Receive_Ack_From_M0(void); static void Receive_Notification_From_M0(void); static void APP_ZIGBEE_ProcessNotifyM0ToM4(void); static void APP_ZIGBEE_ProcessRequestM0ToM4(void); /* USER CODE BEGIN PFP */ static bool APP_ZIGBEE_persist_load(void); static bool APP_ZIGBEE_persist_save(void); static void APP_ZIGBEE_persist_delete(void); static void APP_ZIGBEE_persist_notify_cb(struct ZigBeeT *zb, void *cbarg); static enum ZbStatusCodeT APP_ZIGBEE_ZbStartupPersist(struct ZigBeeT *zb); static void APP_ZIGBEE_PersistCompleted_callback(enum ZbStatusCodeT status,void *arg); #ifdef CFG_NVM static void APP_ZIGBEE_NVM_Init(void); static bool APP_ZIGBEE_NVM_Read(void); static bool APP_ZIGBEE_NVM_Write(void); static void APP_ZIGBEE_NVM_Erase(void); #endif /* CFG_NVM */ /* NVM variables */ /* cache in uninit RAM to store/retrieve persistent data */ union cache { uint8_t U8_data[ST_PERSIST_MAX_ALLOC_SZ]; // in bytes uint32_t U32_data[ST_PERSIST_MAX_ALLOC_SZ/4U]; // in U32 words }; // __attribute__ ((section(".noinit"))) union cache cache_persistent_data; // __attribute__ ((section(".noinit"))) union cache cache_diag_reference; union cache cache_persistent_data; union cache cache_diag_reference; /* timer to delay reading attribute back from persistence */ //static uint8_t TS_ID1; static uint8_t TS_ID2; /* Functions Definition ------------------------------------------------------*/ /* USER CODE END PFP */ /* Private variables ---------------------------------------------------------*/ static TL_CmdPacket_t *p_ZIGBEE_otcmdbuffer; static TL_EvtPacket_t *p_ZIGBEE_notif_M0_to_M4; static TL_EvtPacket_t *p_ZIGBEE_request_M0_to_M4; static __IO uint32_t CptReceiveNotifyFromM0 = 0; static __IO uint32_t CptReceiveRequestFromM0 = 0; PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_ZIGBEE_Config_t ZigbeeConfigBuffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ZigbeeOtCmdBuffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ZigbeeNotifRspEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U]; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ZigbeeNotifRequestBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U]; struct zigbee_app_info { bool has_init; struct ZigBeeT *zb; enum ZbStartType startupControl; enum ZbStatusCodeT join_status; uint32_t join_delay; bool init_after_join; uint32_t persistNumWrites; bool fresh_startup; struct ZbZclClusterT *commissioning_server_1; struct ZbZclClusterT *onOff_server_1; }; static struct zigbee_app_info zigbee_app_info; /* Commissioning server 1 custom callbacks */ static enum ZclStatusCodeT commission_server_1_restart_device(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientRestartDev *req, struct ZbZclAddrInfoT *srcInfo, void *arg); static enum ZclStatusCodeT commission_server_1_save_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientSaveStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg); static enum ZclStatusCodeT commission_server_1_restore_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientRestoreStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg); static enum ZclStatusCodeT commission_server_1_reset_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientResetStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg); static struct ZbZclCommissionServerCallbacksT CommissionServerCallbacks_1 = { .restart_device = commission_server_1_restart_device, .save_startup = commission_server_1_save_startup, .restore_startup = commission_server_1_restore_startup, .reset_startup = commission_server_1_reset_startup, }; /* OnOff server 1 custom callbacks */ static enum ZclStatusCodeT onOff_server_1_off(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg); static enum ZclStatusCodeT onOff_server_1_on(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg); static struct ZbZclOnOffServerCallbacksT OnOffServerCallbacks_1 = { .off = onOff_server_1_off, .on = onOff_server_1_on, }; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Functions Definition ------------------------------------------------------*/ /* Commissioning server restart_device 1 command callback */ static enum ZclStatusCodeT commission_server_1_restart_device(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientRestartDev *req, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 0 Commission server 1 restart_device 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 0 Commission server 1 restart_device 1 */ } /* Commissioning server save_startup 1 command callback */ static enum ZclStatusCodeT commission_server_1_save_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientSaveStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 1 Commission server 1 save_startup 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 1 Commission server 1 save_startup 1 */ } /* Commissioning server restore_startup 1 command callback */ static enum ZclStatusCodeT commission_server_1_restore_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientRestoreStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 2 Commission server 1 restore_startup 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 2 Commission server 1 restore_startup 1 */ } /* Commissioning server reset_startup 1 command callback */ static enum ZclStatusCodeT commission_server_1_reset_startup(struct ZbZclClusterT *cluster, struct ZbZclCommissionClientResetStartup *req, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 3 Commission server 1 reset_startup 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 3 Commission server 1 reset_startup 1 */ } /* OnOff server off 1 command callback */ static enum ZclStatusCodeT onOff_server_1_off(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 4 OnOff server 1 off 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 4 OnOff server 1 off 1 */ } /* OnOff server on 1 command callback */ static enum ZclStatusCodeT onOff_server_1_on(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 5 OnOff server 1 on 1 */ return ZCL_STATUS_SUCCESS; /* USER CODE END 5 OnOff server 1 on 1 */ } /** * @brief Zigbee application initialization * @param None * @retval None */ void APP_ZIGBEE_Init(void) { SHCI_CmdStatus_t ZigbeeInitStatus; APP_DBG("APP_ZIGBEE_Init"); /* Check the compatibility with the Coprocessor Wireless Firmware loaded */ APP_ZIGBEE_CheckWirelessFirmwareInfo(); /* Register cmdbuffer */ APP_ZIGBEE_RegisterCmdBuffer(&ZigbeeOtCmdBuffer); /* Init config buffer and call TL_ZIGBEE_Init */ APP_ZIGBEE_TL_INIT(); /* Register task */ /* Create the different tasks */ UTIL_SEQ_RegTask(1U << (uint32_t)CFG_TASK_NOTIFY_FROM_M0_TO_M4, UTIL_SEQ_RFU, APP_ZIGBEE_ProcessNotifyM0ToM4); UTIL_SEQ_RegTask(1U << (uint32_t)CFG_TASK_REQUEST_FROM_M0_TO_M4, UTIL_SEQ_RFU, APP_ZIGBEE_ProcessRequestM0ToM4); /* Task associated with network creation process */ UTIL_SEQ_RegTask(1U << CFG_TASK_ZIGBEE_NETWORK_FORM, UTIL_SEQ_RFU, APP_ZIGBEE_NwkForm); /* USER CODE BEGIN APP_ZIGBEE_INIT */ /* NVM Init */ #if CFG_NVM APP_ZIGBEE_NVM_Init(); #endif /* USER CODE END APP_ZIGBEE_INIT */ /* Start the Zigbee on the CPU2 side */ ZigbeeInitStatus = SHCI_C2_ZIGBEE_Init(); /* Prevent unused argument(s) compilation warning */ UNUSED(ZigbeeInitStatus); /* Initialize Zigbee stack layers */ APP_ZIGBEE_StackLayersInit(); } /** * @brief Initialize Zigbee stack layers * @param None * @retval None */ static void APP_ZIGBEE_StackLayersInit(void) { APP_DBG("APP_ZIGBEE_StackLayersInit"); zigbee_app_info.zb = ZbInit(0U, NULL, NULL); assert(zigbee_app_info.zb != NULL); /* Create the endpoint and cluster(s) */ APP_ZIGBEE_ConfigEndpoints(); /* USER CODE BEGIN APP_ZIGBEE_StackLayersInit */ /* USER CODE END APP_ZIGBEE_StackLayersInit */ /* Configure the joining parameters */ zigbee_app_info.join_status = (enum ZbStatusCodeT) 0x01; /* init to error status */ zigbee_app_info.join_delay = HAL_GetTick(); /* now */ zigbee_app_info.startupControl = ZbStartTypeJoin; /* STEP 1 - TRY to START FROM PERSISTENCE */ /* define if we need to do a fresh start */ zigbee_app_info.fresh_startup = true; /* First we disable the persistent notification */ ZbPersistNotifyRegister(zigbee_app_info.zb,NULL,NULL); /* Call a startup from persistence */ enum ZbStatusCodeT status = APP_ZIGBEE_ZbStartupPersist(zigbee_app_info.zb); if(status == ZB_STATUS_SUCCESS) { /* no fresh stratup need anymore */ zigbee_app_info.fresh_startup = false; APP_DBG("ZbStartupPersist: SUCCESS, restarted from persistence"); } else { /* Start-up form persistence failed perform a fresh ZbStartup */ APP_DBG("ZbStartupPersist: FAILED to restart from persistence with status: 0x%02x",status); } if(zigbee_app_info.fresh_startup) { /* Go for fresh start */ UTIL_SEQ_SetTask(1U << CFG_TASK_ZIGBEE_NETWORK_FORM, CFG_SCH_PRIO_0); } } /** * @brief Configure Zigbee application endpoints * @param None * @retval None */ static void APP_ZIGBEE_ConfigEndpoints(void) { struct ZbApsmeAddEndpointReqT req; struct ZbApsmeAddEndpointConfT conf; memset(&req, 0, sizeof(req)); /* Endpoint: SW1_ENDPOINT */ req.profileId = ZCL_PROFILE_HOME_AUTOMATION; req.deviceId = ZCL_DEVICE_ONOFF_SWITCH; req.endpoint = SW1_ENDPOINT; ZbZclAddEndpoint(zigbee_app_info.zb, &req, &conf); assert(conf.status == ZB_STATUS_SUCCESS); /* Commissioning server */ zigbee_app_info.commissioning_server_1 = ZbZclCommissionServerAlloc(zigbee_app_info.zb, COMMISSIONING_DEST_ENDPOINT_1, ZCL_PROFILE_HOME_AUTOMATION, APS_SECURED_1, &CommissionServerCallbacks_1, NULL); assert(zigbee_app_info.commissioning_server_1 != NULL); ZbZclClusterEndpointRegister(zigbee_app_info.commissioning_server_1); /* OnOff server */ zigbee_app_info.onOff_server_1 = ZbZclOnOffServerAlloc(zigbee_app_info.zb, SW1_ENDPOINT, &OnOffServerCallbacks_1, NULL); assert(zigbee_app_info.onOff_server_1 != NULL); ZbZclClusterEndpointRegister(zigbee_app_info.onOff_server_1); /* USER CODE BEGIN CONFIG_ENDPOINT */ /* USER CODE END CONFIG_ENDPOINT */ } /** * @brief Handle Zigbee network forming and joining * @param None * @retval None */ static void APP_ZIGBEE_NwkForm(void) { if ((zigbee_app_info.join_status != ZB_STATUS_SUCCESS) && (HAL_GetTick() >= zigbee_app_info.join_delay)) { struct ZbStartupT config; enum ZbStatusCodeT status; /* Configure Zigbee Logging */ ZbSetLogging(zigbee_app_info.zb, ZB_LOG_MASK_LEVEL_5, NULL); /* Attempt to join a zigbee network */ ZbStartupConfigGetProDefaults(&config); /* Set the centralized network */ APP_DBG("Network config : APP_STARTUP_CENTRALIZED_END_DEVICE"); config.startupControl = zigbee_app_info.startupControl; /* Using the default HA preconfigured Link Key */ memcpy(config.security.preconfiguredLinkKey, sec_key_ha, ZB_SEC_KEYSIZE); config.channelList.count = 1; config.channelList.list[0].page = 0; config.channelList.list[0].channelMask = 1 << CHANNEL; /*Channel in use */ /* Add End device configuration */ config.capability |= (MCP_ASSOC_CAP_RXONIDLE | MCP_ASSOC_CAP_DEV_TYPE | MCP_ASSOC_CAP_ALT_COORD); config.capability &= (MCP_ASSOC_CAP_DEV_TYPE); config.endDeviceTimeout=0xFF; /* Using ZbStartupWait (blocking) */ status = ZbStartupWait(zigbee_app_info.zb, &config); APP_DBG("ZbStartup Callback (status = 0x%02x)", status); zigbee_app_info.join_status = status; if (status == ZB_STATUS_SUCCESS) { zigbee_app_info.join_delay = 0U; zigbee_app_info.init_after_join = true; APP_DBG("Startup done !\n"); /* USER CODE BEGIN 6 */ /* USER CODE END 6 */ } else { APP_DBG("Startup failed, attempting again after a short delay (%d ms)", APP_ZIGBEE_STARTUP_FAIL_DELAY); zigbee_app_info.join_delay = HAL_GetTick() + APP_ZIGBEE_STARTUP_FAIL_DELAY; /* USER CODE BEGIN 7 */ /* USER CODE END 7 */ } } /* If Network forming/joining was not successful reschedule the current task to retry the process */ if (zigbee_app_info.join_status != ZB_STATUS_SUCCESS) { UTIL_SEQ_SetTask(1U << CFG_TASK_ZIGBEE_NETWORK_FORM, CFG_SCH_PRIO_0); } /* USER CODE BEGIN NW_FORM */ /* USER CODE END NW_FORM */ } /************************************************************* * ZbStartupWait Blocking Call *************************************************************/ struct ZbStartupWaitInfo { bool active; enum ZbStatusCodeT status; }; static void ZbStartupWaitCb(enum ZbStatusCodeT status, void *cb_arg) { struct ZbStartupWaitInfo *info = cb_arg; info->status = status; info->active = false; UTIL_SEQ_SetEvt(EVENT_ZIGBEE_STARTUP_ENDED); } enum ZbStatusCodeT ZbStartupWait(struct ZigBeeT *zb, struct ZbStartupT *config) { struct ZbStartupWaitInfo *info; enum ZbStatusCodeT status; info = malloc(sizeof(struct ZbStartupWaitInfo)); if (info == NULL) { return ZB_STATUS_ALLOC_FAIL; } memset(info, 0, sizeof(struct ZbStartupWaitInfo)); info->active = true; status = ZbStartup(zb, config, ZbStartupWaitCb, info); if (status != ZB_STATUS_SUCCESS) { free(info); return status; } UTIL_SEQ_WaitEvt(EVENT_ZIGBEE_STARTUP_ENDED); status = info->status; free(info); return status; } /** * @brief Trace the error or the warning reported. * @param ErrId : * @param ErrCode * @retval None */ void APP_ZIGBEE_Error(uint32_t ErrId, uint32_t ErrCode) { switch (ErrId) { default: APP_ZIGBEE_TraceError("ERROR Unknown ", 0); break; } } /************************************************************* * * LOCAL FUNCTIONS * *************************************************************/ /** * @brief Warn the user that an error has occurred. * * @param pMess : Message associated to the error. * @param ErrCode: Error code associated to the module (Zigbee or other module if any) * @retval None */ static void APP_ZIGBEE_TraceError(const char *pMess, uint32_t ErrCode) { APP_DBG("**** Fatal error = %s (Err = %d)", pMess, ErrCode); /* USER CODE BEGIN TRACE_ERROR */ /* USER CODE END TRACE_ERROR */ } /** * @brief Check if the Coprocessor Wireless Firmware loaded supports Zigbee * and display associated information * @param None * @retval None */ static void APP_ZIGBEE_CheckWirelessFirmwareInfo(void) { WirelessFwInfo_t wireless_info_instance; WirelessFwInfo_t *p_wireless_info = &wireless_info_instance; if (SHCI_GetWirelessFwInfo(p_wireless_info) != SHCI_Success) { APP_ZIGBEE_Error((uint32_t)ERR_ZIGBEE_CHECK_WIRELESS, (uint32_t)ERR_INTERFACE_FATAL); } else { APP_DBG("**********************************************************"); APP_DBG("WIRELESS COPROCESSOR FW:"); /* Print version */ APP_DBG("VERSION ID = %d.%d.%d", p_wireless_info->VersionMajor, p_wireless_info->VersionMinor, p_wireless_info->VersionSub); switch (p_wireless_info->StackType) { case INFO_STACK_TYPE_ZIGBEE_FFD: APP_DBG("FW Type : FFD Zigbee stack"); break; case INFO_STACK_TYPE_ZIGBEE_RFD: APP_DBG("FW Type : RFD Zigbee stack"); break; default: /* No Zigbee device supported ! */ APP_ZIGBEE_Error((uint32_t)ERR_ZIGBEE_CHECK_WIRELESS, (uint32_t)ERR_INTERFACE_FATAL); break; } /* print the application name */ char *__PathProject__ = (strstr(__FILE__, "Zigbee") ? strstr(__FILE__, "Zigbee") + 7 : __FILE__); char *pdel = NULL; if((strchr(__FILE__, '/')) == NULL) { pdel = strchr(__PathProject__, '\\'); } else { pdel = strchr(__PathProject__, '/'); } int index = (int)(pdel - __PathProject__); APP_DBG("Application flashed: %*.*s", index, index, __PathProject__); /* print channel */ APP_DBG("Channel used: %d", CHANNEL); /* print Link Key */ APP_DBG("Link Key: %.16s", sec_key_ha); /* print Link Key value hex */ char Z09_LL_string[ZB_SEC_KEYSIZE*3+1]; Z09_LL_string[0] = 0; for (int str_index = 0; str_index < ZB_SEC_KEYSIZE; str_index++) { sprintf(&Z09_LL_string[str_index*3], "%02x ", sec_key_ha[str_index]); } APP_DBG("Link Key value: %s", Z09_LL_string); /* print clusters allocated */ APP_DBG("Clusters allocated are:"); APP_DBG("commissioning Server on Endpoint %d", SW1_ENDPOINT); APP_DBG("onOff Server on Endpoint %d", SW1_ENDPOINT); APP_DBG("**********************************************************"); } } /************************************************************* * * WRAP FUNCTIONS * *************************************************************/ void APP_ZIGBEE_RegisterCmdBuffer(TL_CmdPacket_t *p_buffer) { p_ZIGBEE_otcmdbuffer = p_buffer; } Zigbee_Cmd_Request_t * ZIGBEE_Get_OTCmdPayloadBuffer(void) { return (Zigbee_Cmd_Request_t *)p_ZIGBEE_otcmdbuffer->cmdserial.cmd.payload; } Zigbee_Cmd_Request_t * ZIGBEE_Get_OTCmdRspPayloadBuffer(void) { return (Zigbee_Cmd_Request_t *)((TL_EvtPacket_t *)p_ZIGBEE_otcmdbuffer)->evtserial.evt.payload; } Zigbee_Cmd_Request_t * ZIGBEE_Get_NotificationPayloadBuffer(void) { return (Zigbee_Cmd_Request_t *)(p_ZIGBEE_notif_M0_to_M4)->evtserial.evt.payload; } Zigbee_Cmd_Request_t * ZIGBEE_Get_M0RequestPayloadBuffer(void) { return (Zigbee_Cmd_Request_t *)(p_ZIGBEE_request_M0_to_M4)->evtserial.evt.payload; } /** * @brief This function is used to transfer the commands from the M4 to the M0. * * @param None * @return None */ void ZIGBEE_CmdTransfer(void) { Zigbee_Cmd_Request_t *cmd_req = (Zigbee_Cmd_Request_t *)p_ZIGBEE_otcmdbuffer->cmdserial.cmd.payload; /* Zigbee OT command cmdcode range 0x280 .. 0x3DF = 352 */ p_ZIGBEE_otcmdbuffer->cmdserial.cmd.cmdcode = 0x280U; /* Size = otCmdBuffer->Size (Number of OT cmd arguments : 1 arg = 32bits so multiply by 4 to get size in bytes) * + ID (4 bytes) + Size (4 bytes) */ p_ZIGBEE_otcmdbuffer->cmdserial.cmd.plen = 8U + (cmd_req->Size * 4U); TL_ZIGBEE_SendM4RequestToM0(); /* Wait completion of cmd */ Wait_Getting_Ack_From_M0(); } /** * @brief This function is called when the M0+ acknowledge the fact that it has received a Cmd * * * @param Otbuffer : a pointer to TL_EvtPacket_t * @return None */ void TL_ZIGBEE_CmdEvtReceived(TL_EvtPacket_t *Otbuffer) { /* Prevent unused argument(s) compilation warning */ UNUSED(Otbuffer); Receive_Ack_From_M0(); } /** * @brief This function is called when notification from M0+ is received. * * @param Notbuffer : a pointer to TL_EvtPacket_t * @return None */ void TL_ZIGBEE_NotReceived(TL_EvtPacket_t *Notbuffer) { p_ZIGBEE_notif_M0_to_M4 = Notbuffer; Receive_Notification_From_M0(); } /** * @brief This function is called before sending any ot command to the M0 * core. The purpose of this function is to be able to check if * there are no notifications coming from the M0 core which are * pending before sending a new ot command. * @param None * @retval None */ void Pre_ZigbeeCmdProcessing(void) { UTIL_SEQ_WaitEvt(EVENT_SYNCHRO_BYPASS_IDLE); } /** * @brief This function waits for getting an acknowledgment from the M0. * * @param None * @retval None */ static void Wait_Getting_Ack_From_M0(void) { UTIL_SEQ_WaitEvt(EVENT_ACK_FROM_M0_EVT); } /** * @brief Receive an acknowledgment from the M0+ core. * Each command send by the M4 to the M0 are acknowledged. * This function is called under interrupt. * @param None * @retval None */ static void Receive_Ack_From_M0(void) { UTIL_SEQ_SetEvt(EVENT_ACK_FROM_M0_EVT); } /** * @brief Receive a notification from the M0+ through the IPCC. * This function is called under interrupt. * @param None * @retval None */ static void Receive_Notification_From_M0(void) { CptReceiveNotifyFromM0++; UTIL_SEQ_SetTask(1U << (uint32_t)CFG_TASK_NOTIFY_FROM_M0_TO_M4, CFG_SCH_PRIO_0); } /** * @brief This function is called when a request from M0+ is received. * * @param Notbuffer : a pointer to TL_EvtPacket_t * @return None */ void TL_ZIGBEE_M0RequestReceived(TL_EvtPacket_t *Reqbuffer) { p_ZIGBEE_request_M0_to_M4 = Reqbuffer; CptReceiveRequestFromM0++; UTIL_SEQ_SetTask(1U << (uint32_t)CFG_TASK_REQUEST_FROM_M0_TO_M4, CFG_SCH_PRIO_0); } /** * @brief Perform initialization of TL for Zigbee. * @param None * @retval None */ void APP_ZIGBEE_TL_INIT(void) { ZigbeeConfigBuffer.p_ZigbeeOtCmdRspBuffer = (uint8_t *)&ZigbeeOtCmdBuffer; ZigbeeConfigBuffer.p_ZigbeeNotAckBuffer = (uint8_t *)ZigbeeNotifRspEvtBuffer; ZigbeeConfigBuffer.p_ZigbeeNotifRequestBuffer = (uint8_t *)ZigbeeNotifRequestBuffer; TL_ZIGBEE_Init(&ZigbeeConfigBuffer); } /** * @brief Process the messages coming from the M0. * @param None * @retval None */ static void APP_ZIGBEE_ProcessNotifyM0ToM4(void) { if (CptReceiveNotifyFromM0 != 0) { /* Reset counter */ CptReceiveNotifyFromM0 = 0; Zigbee_CallBackProcessing(); } } /** * @brief Process the requests coming from the M0. * @param None * @retval None */ static void APP_ZIGBEE_ProcessRequestM0ToM4(void) { if (CptReceiveRequestFromM0 != 0) { CptReceiveRequestFromM0 = 0; Zigbee_M0RequestProcessing(); } } /* USER CODE BEGIN FD_LOCAL_FUNCTIONS */ // NVM Functions /** * @brief notify to save persistent data callback * @param zb: Zigbee device object pointer, cbarg: callback arg pointer * @retval None */ static void APP_ZIGBEE_persist_notify_cb(struct ZigBeeT *zb, void *cbarg) { APP_DBG("Notification to save persistent data requested from stack"); /* Save the persistent data */ APP_ZIGBEE_persist_save(); } /** * @brief Start Zigbee Network from persistent data * @param zb: Zigbee device object pointer * @retval Zigbee stack Status code */ static enum ZbStatusCodeT APP_ZIGBEE_ZbStartupPersist(struct ZigBeeT* zb) { bool read_status; enum ZbStatusCodeT status = ZB_STATUS_SUCCESS; /* Restore persistence */ read_status = APP_ZIGBEE_persist_load(); if (read_status) { /* Make sure the EPID is cleared, before we are allowed to restore persistence */ uint64_t epid = 0U; ZbNwkSet(zb, ZB_NWK_NIB_ID_ExtendedPanId, &epid, sizeof(uint64_t)); /* Start-up from persistence */ APP_DBG("APP_ZIGBEE_ZbStartupPersist: restoring stack persistence"); status = ZbStartupPersist(zb, &cache_persistent_data.U8_data[4], cache_persistent_data.U32_data[0],NULL,APP_ZIGBEE_PersistCompleted_callback,NULL); } else { /* Failed to restart from persistence */ APP_DBG("APP_ZIGBEE_ZbStartupPersist: no persistence data to restore"); status = ZB_STATUS_ALLOC_FAIL; } /* Only for debug purpose, depending of persistent data, following traces could display bytes that are irrelevants to on off cluster */ if(status == ZB_STATUS_SUCCESS) { /* read the last bytes of data where the ZCL on off persistent data shall be*/ uint32_t len = cache_persistent_data.U32_data[0] + 4 ; APP_DBG("ClusterID %02x %02x",cache_persistent_data.U8_data[len-9],cache_persistent_data.U8_data[len-10]); APP_DBG("Endpoint %02x %02x",cache_persistent_data.U8_data[len-7],cache_persistent_data.U8_data[len-8]); APP_DBG("Direction %02x",cache_persistent_data.U8_data[len-6]); APP_DBG("AttrID %02x %02x",cache_persistent_data.U8_data[len-4],cache_persistent_data.U8_data[len-5]); APP_DBG("Len %02x %02x",cache_persistent_data.U8_data[len-2],cache_persistent_data.U8_data[len-3]); APP_DBG("Value %02x",cache_persistent_data.U8_data[len-1]); } return status; }/* APP_ZIGBEE_ZbStartupPersist */ /** * @brief timer callback to wait end of restore cluster persistence form M0 * @param None * @retval None */ static void APP_ZIGBEE_PersistCompleted_callback(enum ZbStatusCodeT status,void *arg) { if(status == ZB_WPAN_STATUS_SUCCESS) { APP_DBG("Persist complete callback entered with SUCCESS"); /* Restore the on/off value based on persitence loaded */ } else { APP_DBG("Error in persist complete callback %x",status); } /* STEP3 - Activate back the persistent notofacation */ /* Register Persistent data change notification */ ZbPersistNotifyRegister(zigbee_app_info.zb,APP_ZIGBEE_persist_notify_cb,NULL); /* Call the callback once here to save persistence data */ APP_ZIGBEE_persist_notify_cb(zigbee_app_info.zb,NULL); }/* APP_ZIGBEE_PersistCompleted_callback */ /** * @brief Load persistent data * @param None * @retval true if success, false if fail */ static bool APP_ZIGBEE_persist_load(void) { #ifdef CFG_NVM APP_DBG("Retrieving persistent data from FLASH"); return APP_ZIGBEE_NVM_Read(); #else /* Check length range */ if ((cache_persistent_data.U32_data[0] == 0) || (cache_persistent_data.U32_data[0] > ST_PERSIST_MAX_ALLOC_SZ)) { APP_DBG("No data or too large length : %d",cache_persistent_data.U32_data[0]); return false; } return true; #endif /* CFG_NVM */ } /* APP_ZIGBEE_persist_load */ /** * @brief Save persistent data * @param None * @retval true if success , false if fail */ static bool APP_ZIGBEE_persist_save(void) { uint32_t len; /* Clear the RAM cache before saving */ memset(cache_persistent_data.U8_data, 0x00, ST_PERSIST_MAX_ALLOC_SZ); /* Call the satck API t get current persistent data */ len = ZbPersistGet(zigbee_app_info.zb, 0, 0); /* Check Length range */ if (len == 0U) { /* If the persistence length was zero then no data available. */ APP_DBG("APP_ZIGBEE_persist_save: no persistence data to save !"); return false; } if (len > ST_PERSIST_MAX_ALLOC_SZ) { /* if persistence length to big to store */ APP_DBG("APP_ZIGBEE_persist_save: persist size too large for storage (%d)", len); return false; } /* Store in cache the persistent data */ len = ZbPersistGet(zigbee_app_info.zb, &cache_persistent_data.U8_data[ST_PERSIST_FLASH_DATA_OFFSET], len); /* Store in cache the persistent data length */ cache_persistent_data.U32_data[0] = len; zigbee_app_info.persistNumWrites++; APP_DBG("APP_ZIGBEE_persist_save: Persistence written in cache RAM (num writes = %d) len=%d", zigbee_app_info.persistNumWrites, cache_persistent_data.U32_data[0]); #ifdef CFG_NVM if(!APP_ZIGBEE_NVM_Write()) { return false; } APP_DBG("APP_ZIGBEE_persist_save: Persistent data FLASHED"); #endif /* CFG_NVM */ return true; } /* APP_ZIGBEE_persist_save */ /** * @brief Delete persistent data * @param None * @retval None */ static void APP_ZIGBEE_persist_delete(void) { /* Clear RAM cache */ memset(cache_persistent_data.U8_data, 0x00, ST_PERSIST_MAX_ALLOC_SZ); APP_DBG("Persistent Data RAM cache cleared"); #ifdef CFG_NVM APP_DBG("FLASH ERASED"); APP_ZIGBEE_NVM_Erase(); #endif /* CFG_NVM */ } /* APP_ZIGBEE_persist_delete */ #ifdef CFG_NVM /** * @brief Init the NVM * @param None * @retval None */ static void APP_ZIGBEE_NVM_Init(void) { int eeprom_init_status; APP_DBG("Flash starting address = %x",HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS); eeprom_init_status = EE_Init( 0 , HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS ); if(eeprom_init_status != EE_OK) { /* format NVM since init failed */ eeprom_init_status= EE_Init( 1, HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS ); } APP_DBG("EE_init status = %d",eeprom_init_status); } /* APP_ZIGBEE_NVM_Init */ /** *@brief Read the persistent data from NVM * @param None * @retval true if success , false if failed */ static bool APP_ZIGBEE_NVM_Read(void) { uint16_t num_words = 0; bool status = true; int ee_status = 0; HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGSERR | FLASH_FLAG_WRPERR | FLASH_FLAG_OPTVERR); /* Read the data length from cache */ ee_status = EE_Read(0, ZIGBEE_DB_START_ADDR, &cache_persistent_data.U32_data[0]); if (ee_status != EE_OK) { APP_DBG("Read -> persistent data length not found ERASE to be done - Read Stopped"); status = false; } /* Check length is not too big nor zero */ else if((cache_persistent_data.U32_data[0] == 0) || (cache_persistent_data.U32_data[0]> ST_PERSIST_MAX_ALLOC_SZ)) { APP_DBG("No data or too large length : %d", cache_persistent_data.U32_data[0]); status = false; } /* Length is within range */ else { /* Adjust the length to be U32 aligned */ num_words = (uint16_t) (cache_persistent_data.U32_data[0]/4) ; if (cache_persistent_data.U32_data[0] % 4 != 0) { num_words++; } /* copy the read data from Flash to cache including length */ for (uint16_t local_length = 1; local_length <= num_words; local_length++) { if (local_length >= ST_PERSIST_MAX_ALLOC_SZ/4) { APP_DBG("Local length exceeds the size of the cache persistent data!"); status = false; break; } /* read data from first data in U32 unit */ ee_status = EE_Read(0, local_length + ZIGBEE_DB_START_ADDR, &cache_persistent_data.U32_data[local_length] ); if (ee_status != EE_OK) { APP_DBG("Read not found leaving"); status = false; break; } } } HAL_FLASH_Lock(); if(status) { APP_DBG("READ PERSISTENT DATA LEN = %d",cache_persistent_data.U32_data[0]); } return status; } /* APP_ZIGBEE_NVM_Read */ /** * @brief Write the persistent data in NVM * @param None * @retval None */ static bool APP_ZIGBEE_NVM_Write(void) { int ee_status = 0; uint16_t num_words; uint16_t local_current_size; num_words = 1U; /* 1 words for the length */ num_words+= (uint16_t) (cache_persistent_data.U32_data[0]/4); /* Adjust the length to be U32 aligned */ if (cache_persistent_data.U32_data[0] % 4 != 0) { num_words++; } //save data in flash for (local_current_size = 0; local_current_size < num_words; local_current_size++) { ee_status = EE_Write(0, (uint16_t)local_current_size + ZIGBEE_DB_START_ADDR, cache_persistent_data.U32_data[local_current_size]); if (ee_status != EE_OK) { if(ee_status == EE_CLEAN_NEEDED) /* Shall not be there if CFG_EE_AUTO_CLEAN = 1*/ { APP_DBG("CLEAN NEEDED, CLEANING"); EE_Clean(0,0); } else { /* Failed to write , an Erase shall be done */ APP_DBG("APP_ZIGBEE_NVM_Write failed @ %d status %d", local_current_size,ee_status); break; } } } if(ee_status != EE_OK) { APP_DBG("WRITE STOPPED, need a FLASH ERASE"); return false; } APP_DBG("WRITTEN PERSISTENT DATA LEN = %d",cache_persistent_data.U32_data[0]); return true; } /* APP_ZIGBEE_NVM_Write */ /** * @brief Erase the NVM * @param None * @retval None */ static void APP_ZIGBEE_NVM_Erase(void) { EE_Init(1, HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS); /* Erase Flash */ } /* APP_ZIGBEE_NVM_Erase */ #endif /* CFG_NVM */ /* USER CODE END FD_LOCAL_FUNCTIONS */