/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file App/app_zigbee.c * @author MCD Application Team * @brief Zigbee Application. ****************************************************************************** * @attention * * Copyright (c) 2019-2023 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.onoff.h" #include "zigbee.aps.h" #include "zigbee.nwk.h" /* USER CODE BEGIN Includes */ #include "app_config.h" #include "zigbee_port.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 SW1_ENDPOINT 17 /* USER CODE BEGIN PD */ #define SW1_GROUP_ADDR 0x0001 #define APP_ZIGBEE_COORDINATOR_NWK_ADDR 0x0000 /* 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 void APP_ZIGBEE_ConfigGroupAddr(void); static void APP_ZIGBEE_OpenPermitJoin(uint8_t duration_sec); static int APP_ZIGBEE_SlaveReportIndCb(struct ZbApsdeDataIndT *dataInd, void *cb_arg); static void APP_ZIGBEE_SlaveReportConfCb(struct ZbApsdeDataConfT *conf, void *arg); static bool APP_ZIGBEE_DecodeSlaveReport(const uint8_t *payload, uint16_t length, AppSlaveReport_t *report); static void APP_ZIGBEE_EncodeSlaveReport(const AppSlaveReport_t *report, uint8_t *payload); static void APP_ZIGBEE_RequestNetworkRestart(uint32_t now_ms); static enum ZbStartType APP_ZIGBEE_NextSlaveStartupControl(void); /* 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]; uint8_t g_ot_notification_allowed = 0U; 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; struct ZbZclClusterT *onOff_server_1; struct ZbApsFilterT *slave_report_filter; }; static struct zigbee_app_info zigbee_app_info; /* 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 enum ZclStatusCodeT onOff_server_1_toggle(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg); static struct ZbZclOnOffServerCallbacksT OnOffServerCallbacks_1 = { .off = onOff_server_1_off, .on = onOff_server_1_on, .toggle = onOff_server_1_toggle, }; /* USER CODE BEGIN PV */ static uint8_t APP_ZIGBEE_slave_report_payload[8]; static bool APP_ZIGBEE_slave_report_tx_pending = false; static uint32_t APP_ZIGBEE_last_permit_join_ms = 0U; static uint32_t APP_ZIGBEE_tx_fail_streak = 0U; static uint32_t APP_ZIGBEE_last_rejoin_ms = 0U; static uint32_t APP_ZIGBEE_join_watchdog_start_ms = 0U; static bool APP_ZIGBEE_slave_try_rejoin = true; /* USER CODE END PV */ /* Functions Definition ------------------------------------------------------*/ /* OnOff server off 1 command callback */ static enum ZclStatusCodeT onOff_server_1_off(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 0 OnOff server 1 off 1 */ uint8_t endpoint; endpoint = ZbZclClusterGetEndpoint(cluster); if (endpoint == SW1_ENDPOINT) { APP_DBG("LED_RED OFF"); BSP_LED_Off(LED_RED); (void)ZbZclAttrIntegerWrite(cluster, ZCL_ONOFF_ATTR_ONOFF, 0); } else { /* Unknown endpoint */ return ZCL_STATUS_FAILURE; } return ZCL_STATUS_SUCCESS; /* USER CODE END 0 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 1 OnOff server 1 on 1 */ uint8_t endpoint; endpoint = ZbZclClusterGetEndpoint(cluster); if (endpoint == SW1_ENDPOINT) { APP_DBG("LED_RED ON"); BSP_LED_On(LED_RED); (void)ZbZclAttrIntegerWrite(cluster, ZCL_ONOFF_ATTR_ONOFF, 1); } else { /* Unknown endpoint */ return ZCL_STATUS_FAILURE; } return ZCL_STATUS_SUCCESS; /* USER CODE END 1 OnOff server 1 on 1 */ } /* OnOff server toggle 1 command callback */ static enum ZclStatusCodeT onOff_server_1_toggle(struct ZbZclClusterT *cluster, struct ZbZclAddrInfoT *srcInfo, void *arg) { /* USER CODE BEGIN 2 OnOff server 1 toggle 1 */ uint8_t attrVal; if (ZbZclAttrRead(cluster, ZCL_ONOFF_ATTR_ONOFF, NULL, &attrVal, sizeof(attrVal), false) != ZCL_STATUS_SUCCESS) { return ZCL_STATUS_FAILURE; } if (attrVal != 0) { return onOff_server_1_off(cluster, srcInfo, arg); } else { return onOff_server_1_on(cluster, srcInfo, arg); } /* USER CODE END 2 OnOff server 1 toggle 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 */ /* 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 */ BSP_LED_Off(LED_RED); BSP_LED_Off(LED_GREEN); BSP_LED_Off(LED_BLUE); APP_ZIGBEE_join_watchdog_start_ms = HAL_GetTick(); /* 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 = (g_app.role == APP_ROLE_MASTER) ? ZbStartTypeForm : APP_ZIGBEE_NextSlaveStartupControl(); /* Initialization Complete */ zigbee_app_info.has_init = true; /* run the task */ 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); /* 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 */ memset(&req, 0, sizeof(req)); req.profileId = ZCL_PROFILE_HOME_AUTOMATION; req.deviceId = ZCL_DEVICE_ONOFF_SWITCH; req.endpoint = APP_ZIGBEE_ENDPOINT; ZbZclAddEndpoint(zigbee_app_info.zb, &req, &conf); assert(conf.status == ZB_STATUS_SUCCESS); zigbee_app_info.slave_report_filter = ZbApsFilterClusterAdd(zigbee_app_info.zb, APP_ZIGBEE_ENDPOINT, APP_ZIGBEE_CLUSTER_INPUTS, ZCL_PROFILE_HOME_AUTOMATION, APP_ZIGBEE_SlaveReportIndCb, NULL); assert(zigbee_app_info.slave_report_filter != NULL); /* 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 : %s", (g_app.role == APP_ROLE_MASTER) ? "APP_STARTUP_CENTRALIZED_COORDINATOR" : "APP_STARTUP_CENTRALIZED_ROUTER"); config.startupControl = zigbee_app_info.startupControl; config.panId = APP_ZIGBEE_PAN_ID; config.extendedPanId = APP_ZIGBEE_EXTENDED_PAN_ID; /* 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 = 1UL << APP_ZIGBEE_CHANNEL; /*Channel in use */ /* 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; g_app.zigbee_state = APP_ZB_CONNECTED; APP_ZIGBEE_join_watchdog_start_ms = HAL_GetTick(); if (g_app.role == APP_ROLE_SLAVE) { APP_ZIGBEE_slave_try_rejoin = true; } if ((g_app.role == APP_ROLE_SLAVE) && (watch_rejoin_active != 0U)) { watch_rejoin_active = 0U; watch_rejoin_success_count++; watch_rejoin_last_status = (uint32_t)status; } APP_DBG("Startup done !\n"); /* USER CODE BEGIN 3 */ BSP_LED_On(LED_BLUE); /* USER CODE END 3 */ } 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; if (g_app.role == APP_ROLE_SLAVE) { zigbee_app_info.startupControl = APP_ZIGBEE_NextSlaveStartupControl(); } g_app.zigbee_state = APP_ZB_JOINING; if ((g_app.role == APP_ROLE_SLAVE) && (watch_rejoin_active != 0U)) { watch_rejoin_fail_count++; watch_rejoin_last_status = (uint32_t)status; } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ } } /* 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 */ else { zigbee_app_info.init_after_join = false; /* Assign ourselves to the group addresses */ APP_ZIGBEE_ConfigGroupAddr(); if (g_app.role == APP_ROLE_MASTER) { APP_ZIGBEE_OpenPermitJoin(APP_ZIGBEE_PERMIT_JOIN_SEC); APP_ZIGBEE_last_permit_join_ms = HAL_GetTick(); } /* Since we're using group addressing (broadcast), shorten the broadcast timeout */ uint32_t bcast_timeout = 3; ZbNwkSet(zigbee_app_info.zb, ZB_NWK_NIB_ID_NetworkBroadcastDeliveryTime, &bcast_timeout, sizeof(bcast_timeout)); } /* 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 */ while (1U == 1U) { BSP_LED_Toggle(LED1); HAL_Delay(500U); BSP_LED_Toggle(LED2); HAL_Delay(500U); BSP_LED_Toggle(LED3); HAL_Delay(500U); } /* 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", APP_ZIGBEE_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("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 used to transfer the commands from the M4 to the M0 with notification * * @param None * @return None */ void ZIGBEE_CmdTransferWithNotif(void) { g_ot_notification_allowed = 1; ZIGBEE_CmdTransfer(); } /** * @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 */ /** * @brief Set group addressing mode * @param None * @retval None */ static void APP_ZIGBEE_ConfigGroupAddr(void) { struct ZbApsmeAddGroupReqT req; struct ZbApsmeAddGroupConfT conf; memset(&req, 0, sizeof(req)); req.endpt = SW1_ENDPOINT; req.groupAddr = SW1_GROUP_ADDR; ZbApsmeAddGroupReq(zigbee_app_info.zb, &req, &conf); memset(&req, 0, sizeof(req)); req.endpt = APP_ZIGBEE_ENDPOINT; req.groupAddr = SW1_GROUP_ADDR; ZbApsmeAddGroupReq(zigbee_app_info.zb, &req, &conf); } /** * @brief Open the coordinator/router network for slave joining. * @param duration_sec Permit-join duration in seconds, 0 closes, 0xff keeps open. * @retval None */ static void APP_ZIGBEE_OpenPermitJoin(uint8_t duration_sec) { struct ZbNlmePermitJoinReqT req; struct ZbNlmePermitJoinConfT conf; memset(&req, 0, sizeof(req)); req.permitDuration = duration_sec; ZbNlmePermitJoinReq(zigbee_app_info.zb, &req, &conf); watch_permit_join_count++; watch_permit_join_status = (uint32_t)conf.status; watch_permit_join_duration = (uint32_t)duration_sec; APP_DBG("Permit join %d sec status = 0x%02x", duration_sec, conf.status); } bool APP_ZIGBEE_IsReady(void) { const bool ready = (zigbee_app_info.has_init && (zigbee_app_info.join_status == ZB_STATUS_SUCCESS) && (g_app.zigbee_state == APP_ZB_CONNECTED)); watch_zigbee_ready = ready ? 1U : 0U; return ready; } void APP_ZIGBEE_Process(void) { const uint32_t now = HAL_GetTick(); if ((g_app.role == APP_ROLE_MASTER) && APP_ZIGBEE_IsReady() && ((now - APP_ZIGBEE_last_permit_join_ms) >= APP_ZIGBEE_PERMIT_REFRESH_MS)) { APP_ZIGBEE_OpenPermitJoin(APP_ZIGBEE_PERMIT_JOIN_SEC); APP_ZIGBEE_last_permit_join_ms = now; } if ((g_app.role == APP_ROLE_SLAVE) && zigbee_app_info.has_init && (g_app.zigbee_state != APP_ZB_CONNECTED) && ((now - APP_ZIGBEE_join_watchdog_start_ms) >= APP_ZIGBEE_JOIN_WATCHDOG_MS)) { watch_join_watchdog_count++; APP_ZIGBEE_RequestNetworkRestart(now); } if ((g_app.role == APP_ROLE_SLAVE) && APP_ZIGBEE_IsReady() && (APP_ZIGBEE_tx_fail_streak >= APP_ZIGBEE_REJOIN_FAIL_COUNT)) { APP_ZIGBEE_RequestNetworkRestart(now); } } static void APP_ZIGBEE_RequestNetworkRestart(uint32_t now_ms) { if ((now_ms - APP_ZIGBEE_last_rejoin_ms) < APP_ZIGBEE_REJOIN_RETRY_MS) { return; } APP_ZIGBEE_last_rejoin_ms = now_ms; watch_rejoin_request_count++; watch_rejoin_active = 1U; g_app.zigbee_state = APP_ZB_JOINING; zigbee_app_info.join_status = (enum ZbStatusCodeT)0x01; zigbee_app_info.join_delay = now_ms; zigbee_app_info.init_after_join = false; zigbee_app_info.startupControl = APP_ZIGBEE_NextSlaveStartupControl(); APP_ZIGBEE_join_watchdog_start_ms = now_ms; APP_ZIGBEE_tx_fail_streak = 0U; APP_ZIGBEE_slave_report_tx_pending = false; watch_report_tx_fail_streak = 0U; ZbReset(zigbee_app_info.zb); UTIL_SEQ_SetTask(1U << CFG_TASK_ZIGBEE_NETWORK_FORM, CFG_SCH_PRIO_0); } static enum ZbStartType APP_ZIGBEE_NextSlaveStartupControl(void) { const enum ZbStartType startup = APP_ZIGBEE_slave_try_rejoin ? ZbStartTypeRejoin : ZbStartTypeJoin; APP_ZIGBEE_slave_try_rejoin = !APP_ZIGBEE_slave_try_rejoin; return startup; } bool APP_ZIGBEE_SendSlaveReport(const AppSlaveReport_t *report) { struct ZbApsdeDataReqT req; enum ZbStatusCodeT status; if ((report == NULL) || !APP_ZIGBEE_IsReady() || APP_ZIGBEE_slave_report_tx_pending) { watch_report_tx_busy_count++; return false; } watch_report_tx_attempt_count++; APP_ZIGBEE_EncodeSlaveReport(report, APP_ZIGBEE_slave_report_payload); memset(&req, 0, sizeof(req)); req.dst.mode = ZB_APSDE_ADDRMODE_SHORT; req.dst.nwkAddr = APP_ZIGBEE_COORDINATOR_NWK_ADDR; req.dst.endpoint = APP_ZIGBEE_ENDPOINT; req.profileId = ZCL_PROFILE_HOME_AUTOMATION; req.clusterId = APP_ZIGBEE_CLUSTER_INPUTS; req.srcEndpt = APP_ZIGBEE_ENDPOINT; req.asdu = APP_ZIGBEE_slave_report_payload; req.asduLength = sizeof(APP_ZIGBEE_slave_report_payload); req.txOptions = ZB_APSDE_DATAREQ_TXOPTIONS_SECURITY | ZB_APSDE_DATAREQ_TXOPTIONS_ACK; req.discoverRoute = true; req.radius = 0U; status = ZbApsdeDataReqCallback(zigbee_app_info.zb, &req, APP_ZIGBEE_SlaveReportConfCb, NULL); if (status == ZB_STATUS_SUCCESS) { APP_ZIGBEE_slave_report_tx_pending = true; watch_report_tx_ok_count++; } else { watch_report_tx_busy_count++; watch_report_tx_confirm_status = (uint32_t)status; } return (status == ZB_STATUS_SUCCESS); } static int APP_ZIGBEE_SlaveReportIndCb(struct ZbApsdeDataIndT *dataInd, void *cb_arg) { AppSlaveReport_t report; (void)cb_arg; watch_report_rx_ind_count++; if (dataInd == NULL) { watch_report_rx_decode_fail_count++; return ZB_STATUS_SUCCESS; } watch_report_rx_last_cluster = dataInd->clusterId; watch_report_rx_last_length = dataInd->asduLength; if ((dataInd->clusterId != APP_ZIGBEE_CLUSTER_INPUTS) || !APP_ZIGBEE_DecodeSlaveReport(dataInd->asdu, dataInd->asduLength, &report)) { watch_report_rx_decode_fail_count++; return ZB_STATUS_SUCCESS; } ZigbeePort_OnSlaveReportReceived(&report, HAL_GetTick()); return ZB_STATUS_SUCCESS; } static void APP_ZIGBEE_SlaveReportConfCb(struct ZbApsdeDataConfT *conf, void *arg) { (void)arg; APP_ZIGBEE_slave_report_tx_pending = false; watch_report_tx_confirm_count++; if ((conf != NULL) && (conf->status != ZB_STATUS_SUCCESS)) { APP_ZIGBEE_tx_fail_streak++; watch_report_tx_fail_streak = APP_ZIGBEE_tx_fail_streak; watch_report_tx_confirm_status = (uint32_t)conf->status; APP_DBG("Slave report tx status = 0x%02x", conf->status); } else if (conf != NULL) { APP_ZIGBEE_tx_fail_streak = 0U; watch_report_tx_fail_streak = 0U; watch_report_tx_confirm_status = (uint32_t)conf->status; } } static bool APP_ZIGBEE_DecodeSlaveReport(const uint8_t *payload, uint16_t length, AppSlaveReport_t *report) { if ((payload == NULL) || (report == NULL) || (length < 8U)) { return false; } report->sequence = ((uint32_t)payload[0]) | ((uint32_t)payload[1] << 8) | ((uint32_t)payload[2] << 16) | ((uint32_t)payload[3] << 24); report->button_mask = payload[4]; report->analog_raw = ((uint16_t)payload[5]) | ((uint16_t)payload[6] << 8); report->analog_percent = payload[7]; return true; } static void APP_ZIGBEE_EncodeSlaveReport(const AppSlaveReport_t *report, uint8_t *payload) { payload[0] = (uint8_t)(report->sequence); payload[1] = (uint8_t)(report->sequence >> 8); payload[2] = (uint8_t)(report->sequence >> 16); payload[3] = (uint8_t)(report->sequence >> 24); payload[4] = report->button_mask; payload[5] = (uint8_t)(report->analog_raw); payload[6] = (uint8_t)(report->analog_raw >> 8); payload[7] = report->analog_percent; } /* USER CODE END FD_LOCAL_FUNCTIONS */