diff options
Diffstat (limited to 'Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE_Zigbee/BLE_Zigbee_Dyn/STM32_WPAN/App/app_zigbee.c')
-rw-r--r-- | Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE_Zigbee/BLE_Zigbee_Dyn/STM32_WPAN/App/app_zigbee.c | 1170 |
1 files changed, 1170 insertions, 0 deletions
diff --git a/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE_Zigbee/BLE_Zigbee_Dyn/STM32_WPAN/App/app_zigbee.c b/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE_Zigbee/BLE_Zigbee_Dyn/STM32_WPAN/App/app_zigbee.c new file mode 100644 index 000000000..3e4261f9c --- /dev/null +++ b/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE_Zigbee/BLE_Zigbee_Dyn/STM32_WPAN/App/app_zigbee.c @@ -0,0 +1,1170 @@ +/** + ****************************************************************************** + * File Name : App/app_zigbee.c + * Description : Zigbee Application. + ****************************************************************************** + * @attention + * + * <h2><center>© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.</center></h2> + * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* 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" +#include "app_ble.h" +#include "stm32_lpm.h" + +#include <assert.h> +#include "zcl/zcl.h" +#include "zcl/zcl.onoff.h" +#include "zcl/zcl.identify.h" + +/* Private defines -----------------------------------------------------------*/ +#define APP_ZIGBEE_STARTUP_FAIL_DELAY 500U +#define SW1_ENDPOINT 17U +#define SW1_GROUP_ADDR 0x0001 +#define CHANNEL 13U +#define CHANNELMASK_12TO14 0x00007000U /* Channels 12-14 */ +#define SED_SLEEP_TIME_30S 1 + +#define COLOR_DEFAULT "\x1b[0m" +#define COL_CYAN "\x1b[0;96m" +#define COL_MAGENTA "\x1b[0;95m" +#define COL_RED "\x1b[0;91m" +#define COL_NORM "\x1b[0m" + +#define DELAY_5_MS (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */ +#define DELAY_20_MS (0.02*1000*1000/CFG_TS_TICK_VAL) /**< 20ms */ +#define DELAY_40_MS (0.04*1000*1000/CFG_TS_TICK_VAL) /**< 40ms */ +#define DELAY_50_MS (0.05*1000*1000/CFG_TS_TICK_VAL) /**< 50ms */ +#define DELAY_100_MS (100*1000/CFG_TS_TICK_VAL) /**< 100 ms */ +#define DELAY_300_MS (300000/CFG_TS_TICK_VAL) /**< 500 ms */ +#define DELAY_500_MS (0.5*1000*1000/CFG_TS_TICK_VAL) /**< 500 ms */ +#define DELAY_1_S (1000*1000/CFG_TS_TICK_VAL) /**< 1 s */ + +#undef TRACE_ZB_FULL_NNT /* undef or comment for public version */ + +#undef STRESS_TEST /* undef or comment for public version */ +#ifdef STRESS_TEST +#define TOGGLE_INTERVAL DELAY_20_MS /* 20 ms interval between 2 toggles */ +#else +#define TOGGLE_INTERVAL DELAY_1_S /* 1 sec interval between 2 toggles */ +#endif +#define DBG_NWK_STATUS_INTERVAL (DELAY_1_S * 15) /* 15 sec interval to display stats */ + + +#define OUTPUT_LINE_MAX_LEN 256 + +/****************************************************************************** + * Persistence + ****************************************************************************** + */ +/* For certification testing, we need a little more than 2K for one of the tests. + * Set to 4K to be safe. 4K is enough for a Coordinator to persist an 80-node + * network. */ +#define ST_PERSIST_MAX_ALLOC_SZ (4U * 1024U) +#define ST_PERSIST_FLASH_DATA_OFFSET 4U + +/* Private function prototypes -----------------------------------------------*/ +static void APP_ZIGBEE_StackLayersInit(void); +static void APP_ZIGBEE_ConfigEndpoints(void); +static void APP_ZIGBEE_SW1_Process(void); +static void APP_ZIGBEE_OnOff_Toggle(void); +static void APP_ZIGBEE_Process_OnOff_Toggle(); + +static void APP_ZIGBEE_DbgNwkStatus(); +static void APP_ZIGBEE_Status_Nnt(void); + +static void APP_ZIGBEE_NwkForm(void); +static void APP_ZIGBEE_ConfigGroupAddr(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); + + +#ifdef WITH_PERSISTANT +static const void * APP_ZIGBEE_persist_load(unsigned int *bufLen); +static void APP_ZIGBEE_persist_delete(void); +static void APP_ZIGBEE_persist_buf_free(const void *buf); +#endif // WITH_PERSISTANT + +static uint32_t APP_ZIGBEE_GetStartNb(void); +static void APP_ZIGBEE_IncrementStartNb(void); +static void toggle_cb(struct ZbZclCommandRspT *rsp, void *arg); + +/* 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; + + struct ZbZclClusterT *onoff_client_1; +}; +static struct zigbee_app_info zigbee_app_info; + +static uint32_t join_start_time; +static double join_time_duration; + +static bool ble_sync_request = FALSE; + +#ifdef STRESS_TEST +static uint32_t time_start; +#endif + +/* Public variables -----------------------------------------------*/ +uint8_t ZbStackType; /* ZB stack type, static or dynamic, FFD or RFD */ +uint32_t toggle_cnt = 0; +uint32_t toggle_fail = 0; +uint32_t disc_cnt = 0; +uint8_t Timer_ToggleOnOff_Id; +uint8_t Timer_DbgNwkStatus_Id; + +/* Keep track of number of Zigbee start */ +static uint8_t zigbee_start_nb = 0U; +static bool zigbee_logging_done = FALSE; + +/* Functions Definition ------------------------------------------------------*/ +/* external definition */ +enum ZbStatusCodeT ZbStartupWait(struct ZigBeeT *zb, struct ZbStartupT *config); + +void APP_ZIGBEE_Init(void) +{ + SHCI_CmdStatus_t ZigbeeInitStatus; + + /* Do not allow stop mode before ZB is initialized */ + UTIL_LPM_SetStopMode(1 << CFG_LPM_APP, UTIL_LPM_DISABLE); + + APP_DBG("APP_ZIGBEE_Init"); + APP_DBG("STARTING ON CHANNEL = %d", CHANNEL); + + /* 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); + + /* Task associated with push button SW1 */ + UTIL_SEQ_RegTask(1U << CFG_TASK_BUTTON_SW1, UTIL_SEQ_RFU, APP_ZIGBEE_SW1_Process); + + /* Task associated with Toggle On/Off */ + UTIL_SEQ_RegTask(1U << CFG_TASK_TOGGLE_ON_OFF, UTIL_SEQ_RFU, APP_ZIGBEE_OnOff_Toggle); + + /* Task associated with Dbg Nwk Status */ +UTIL_SEQ_RegTask(1U << CFG_TASK_DBG_NWK_STATUS, UTIL_SEQ_RFU, APP_ZIGBEE_Status_Nnt); + + /* Init the Zigbee on the CPU2 side */ + ZigbeeInitStatus = SHCI_C2_ZIGBEE_Init(); + /* Prevent unused argument(s) compilation warning */ + UNUSED(ZigbeeInitStatus); + + /** + * Create timer for Toggle On/Off process + */ + HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(Timer_ToggleOnOff_Id), hw_ts_SingleShot, APP_ZIGBEE_Process_OnOff_Toggle); + /** + * Create timer for Network Status process + */ + HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(Timer_DbgNwkStatus_Id), hw_ts_SingleShot, APP_ZIGBEE_DbgNwkStatus); + + /* Initialize Zigbee stack layers and launch network formation */ + APP_ZIGBEE_StackLayersInit(); + + APP_ZIGBEE_IncrementStartNb(); + +} /* APP_ZIGBEE_Init */ + + +void APP_ZIGBEE_Stop(void) +{ + APP_DBG("APP_ZIGBEE_Stop"); + + BSP_LED_Off(LED_RED); + BSP_LED_Off(LED_GREEN); + BSP_LED_Off(LED_BLUE); + + /* Save Persistent data */ + APP_DBG("Save persistent data"); +#ifdef WITH_PERSISTANT + APP_ZIGBEE_persist_save(); +#endif //WITH_PERSISTANT + + /* Zigbee STOP Procedure */ + /* Free memory allocated by Zigbee stack */ + if (zigbee_app_info.zb == NULL) { + return; + } + //ZbIfDetach(zigbee_app_info.zb, &zigbee_app_info.device); + ZbDestroy(zigbee_app_info.zb); + zigbee_app_info.zb = NULL; +} + +/** + * @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(); + + BSP_LED_Off(LED_RED); + BSP_LED_Off(LED_GREEN); + BSP_LED_Off(LED_BLUE); + + /* 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; + + /* Initialization Complete */ + zigbee_app_info.has_init = true; + + join_start_time = HAL_GetTick(); + UTIL_SEQ_SetTask(1U << CFG_TASK_ZIGBEE_NETWORK_FORM, CFG_SCH_PRIO_0); +} + +static void APP_ZIGBEE_ConfigEndpoints(void) +{ + ZbApsmeAddEndpointReqT req; + ZbApsmeAddEndpointConfT conf; + + memset(&req, 0, sizeof(req)); + req.profileId = ZCL_PROFILE_HOME_AUTOMATION; + req.deviceId = ZCL_DEVICE_ONOFF_SWITCH; + + /* Endpoint: SW1_ENDPOINT */ + req.endpoint = SW1_ENDPOINT; + ZbZclAddEndpoint(zigbee_app_info.zb, &req, &conf); + assert(conf.status == ZB_STATUS_SUCCESS); + + /* OnOff Client */ + zigbee_app_info.onoff_client_1 = ZbZclOnOffClientAlloc(zigbee_app_info.zb, SW1_ENDPOINT); + assert(zigbee_app_info.onoff_client_1 != NULL); + ZbZclClusterEndpointRegister(zigbee_app_info.onoff_client_1); + +} /* config_endpoints */ + +/** + * @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; + + if (zigbee_logging_done == FALSE) + { + /* Configure Zigbee Logging (only need to do this once, but this is a good place to put it) */ + ZbSetLogging(zigbee_app_info.zb, ZB_LOG_MASK_LEVEL_5, NULL); + zigbee_logging_done = TRUE; + } + + /* Attempt to join a zigbee network */ + ZbStartupConfigGetProDefaults(&config); + + zigbee_app_info.startupControl = ZbStartTypeJoin; + 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*/ + //config.channelList.list[0].channelMask = CHANNELMASK_12TO14; /* Channels in use*/ + + APP_DBG("Network config : APP_STARTUP_CENTRALIZED_ROUTER"); + + APP_DBG("*** zigbee_start_nb value = %d ***", APP_ZIGBEE_GetStartNb()); +#ifdef WITH_PERSISTANT + if(APP_ZIGBEE_GetStartNb() < 2U) +#endif + { + /* Using ZbStartupWait (blocking) here instead of ZbStartup, in order to demonstrate how to do + * a blocking call on the M4. */ + status = ZbStartupWait(zigbee_app_info.zb, &config); + + APP_DBG("ZbStartup Callback (status = 0x%02x)\n", status); + zigbee_app_info.join_status = status; + + if (status == ZB_STATUS_SUCCESS) { + join_time_duration = (double)(HAL_GetTick() - join_start_time)/1000; + APP_DBG("%s==> JOIN SUCCESS, Duration = (%.2f seconds)%s\n", COL_MAGENTA, join_time_duration, COL_NORM); + zigbee_app_info.join_delay = 0U; + zigbee_app_info.init_after_join = true; + BSP_LED_On(LED_BLUE); + } + 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; + } + } +#ifdef WITH_PERSISTANT + else + { + /* Restart from persistence */ + if (APP_ZIGBEE_ZbStartupPersist(zigbee_app_info.zb) == ZB_STATUS_SUCCESS) + { + APP_DBG("APP_ZIGBEE_ZbStartupPersist SUCCESS!"); + zigbee_app_info.join_status = ZB_STATUS_SUCCESS; + BSP_LED_On(LED_BLUE); + } + else + { + APP_DBG("APP_ZIGBEE_ZbStartupPersist FAILED!"); + } + } +#endif //WITH_PERSISTANT + } + + /* 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); + } + else /* JOIN successful */ + { + zigbee_app_info.init_after_join = false; + + /* Do it only first time */ + if(APP_ZIGBEE_GetStartNb() == 1U) + { + /* Assign ourselves to the group addresses */ + APP_ZIGBEE_ConfigGroupAddr(); + + /* 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)); + + APP_DBG("==> Start_ZB Task Toggle"); + /* Next toggle after TOGGLE_INTERVAL */ + HW_TS_Start(Timer_ToggleOnOff_Id, (uint32_t)TOGGLE_INTERVAL); + + switch (ZbStackType) { /* Check ZB stack type */ + case INFO_STACK_TYPE_BLE_ZIGBEE_RFD_DYNAMIC: /* Start ZB/BLE Dynamic mode */ + case INFO_STACK_TYPE_BLE_ZIGBEE_FFD_DYNAMIC: /* Start ZB/BLE Dynamic mode */ + /* Next network status after DBG_NWK_STATUS_INTERVAL */ + HW_TS_Start(Timer_DbgNwkStatus_Id, (uint32_t)DBG_NWK_STATUS_INTERVAL); + break; + default: + break; + } + } + } +} + +/** + * @brief Set group addressing mode + * @param None + * @retval None + */ +static void APP_ZIGBEE_ConfigGroupAddr(void) +{ + ZbApsmeAddGroupReqT req; + ZbApsmeAddGroupConfT conf; + + memset(&req, 0, sizeof(req)); + req.endpt = SW1_ENDPOINT; + req.groupAddr = SW1_GROUP_ADDR; + ZbApsmeAddGroupReq(zigbee_app_info.zb, &req, &conf); + +} /* APP_ZIGBEE_ConfigGroupAddr */ + +/************************************************************* + * 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; +} /* ZbStartupWaitCb */ + +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) { + info->active = false; + return status; + } + + while (info->active) { + UTIL_SEQ_Run( UTIL_SEQ_DEFAULT ); + } + + status = info->status; + free(info); + return status; +} /* ZbStartupWait */ + + +static void APP_ZIGBEE_IncrementStartNb(void) +{ + zigbee_start_nb++; +} + +static uint32_t APP_ZIGBEE_GetStartNb(void) +{ + return zigbee_start_nb; +} + +/* Persistence */ +/** + * @brief Start Zigbee Network with data from persistent memory + * @param zb : Zigbee Device object + * @retval None + */ +#ifdef WITH_PERSISTANT +enum ZbStatusCodeT APP_ZIGBEE_ZbStartupPersist(struct ZigBeeT *zb) +{ + const void *buf_ptr; + unsigned int buf_len; + enum ZbStatusCodeT status = ZB_STATUS_SUCCESS; + + /* Restore persistence */ + buf_ptr = APP_ZIGBEE_persist_load(&buf_len); + APP_ZIGBEE_persist_delete(); + + if (buf_ptr != NULL) { + APP_DBG("APP_ZIGBEE_ZbStartupPersist: restoring stack persistence"); + /* FIXME 0 - CBKE config? */ + status = ZbStartupPersist(zb, buf_ptr, buf_len, NULL); + APP_ZIGBEE_persist_buf_free(buf_ptr); + + }else + { + status = ZB_STATUS_ALLOC_FAIL; + } + + return status; + +} + +/** + * @brief Load persitent data + * @param bufLen : pointer on buffer length + * @retval None + */ +static const void * APP_ZIGBEE_persist_load(unsigned int *bufLen) +{ + uint8_t *buf; + uint32_t persist_len; + + buf = malloc(ST_PERSIST_MAX_ALLOC_SZ); + if (buf == NULL) { + APP_DBG("APP_ZIGBEE_persist_load : memory exhausted!"); + return NULL; + } + + /* Read the persistence length */ + if (utilsFlashRead(0, buf, ST_PERSIST_FLASH_DATA_OFFSET) != ST_PERSIST_FLASH_DATA_OFFSET) { + APP_DBG("APP_ZIGBEE_persist_load : failed to read length from Flash!"); + return NULL; + } + persist_len = pletoh32(buf); + APP_DBG("ZIGBBE Persistent data length = %d ", persist_len); + if (persist_len > ST_PERSIST_MAX_ALLOC_SZ) { + APP_DBG("APP_ZIGBEE_persist_load : invalid length = %d!", persist_len); + return NULL; + } + + if (utilsFlashRead(ST_PERSIST_FLASH_DATA_OFFSET, buf, persist_len) != persist_len) { + APP_DBG("APP_ZIGBEE_persist_load : failed to read persist data from Flash!"); + return NULL; + } + + APP_DBG("Successfully retrieved data from persistence"); + + *bufLen = persist_len; + return buf; +} + +/** + * @brief Delete first word of persistent data (size = 0 meaning no data) + * @param None + * @retval None + */ +static void APP_ZIGBEE_persist_delete(void) +{ + uint8_t len_buf[ST_PERSIST_FLASH_DATA_OFFSET]; + + putle32(len_buf, 0); + if (utilsFlashWrite(0, len_buf, ST_PERSIST_FLASH_DATA_OFFSET) != ST_PERSIST_FLASH_DATA_OFFSET) { + APP_DBG("APP_ZIGBEE_persist_delete : failed to write flash"); + } +} + +/** + * @brief Free buffer + * @param buf : pointer on buffer to free + * @retval None + */ +static void APP_ZIGBEE_persist_buf_free(const void *buf) +{ + free((void *)buf); +} + +/** + * @brief Save persistent data in FLASH (Or Flash Emulation in RAM) + * @param None + * @retval None + */ +bool APP_ZIGBEE_persist_save(void) +{ + uint8_t *buf; + uint8_t len_buf[ST_PERSIST_FLASH_DATA_OFFSET]; + unsigned int len; + + len = ZbPersistGet(zigbee_app_info.zb, 0, 0); + if (len == 0U) { + /* If the persistence length was zero, then remove the file. */ + APP_DBG("APP_ZIGBEE_persist_save: no persistence data!"); + //cli_persist_delete(cli_p, filename); + return true; + } + if (len > ST_PERSIST_MAX_ALLOC_SZ) { + APP_DBG("APP_ZIGBEE_persist_save: persist size too large for storage (%d)", len); + return false; + } + + buf = malloc(ST_PERSIST_MAX_ALLOC_SZ); + if (buf == NULL) { + APP_DBG("APP_ZIGBEE_persist_save: memory exhausted"); + return false; + } + + len = ZbPersistGet(zigbee_app_info.zb, buf, len); + + /* Write the length */ + putle32(len_buf, len); + if (utilsFlashWrite(0, len_buf, ST_PERSIST_FLASH_DATA_OFFSET) != ST_PERSIST_FLASH_DATA_OFFSET) { + APP_DBG("APP_ZIGBEE_persist_save: failed to write flash"); + free(buf); + return false; + } + + /* Write the persistent data */ + if (utilsFlashWrite(ST_PERSIST_FLASH_DATA_OFFSET, buf, len) != len) { + APP_DBG("APP_ZIGBEE_persist_save: failed to write flash"); + free(buf); + return false; + } + + free(buf); + + zigbee_app_info.persistNumWrites++; + APP_DBG("APP_ZIGBEE_persist_save: Persistence written (num writes = %d)", zigbee_app_info.persistNumWrites); + return true; +} +#endif //WITH_PERSISTANT + + + +/** + * @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; + } +} /* APP_ZIGBEE_Error */ + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ + +/** + * @brief Warn the user that an error has occurred.In this case, + * the LEDs on the Board will start blinking. + * + * @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); + while (1U == 1U) { + BSP_LED_Toggle(LED1); + HAL_Delay(500U); + BSP_LED_Toggle(LED2); + HAL_Delay(500U); + BSP_LED_Toggle(LED3); + HAL_Delay(500U); + } +} /* APP_ZIGBEE_TraceError */ + +/** + * @brief Check if the Coprocessor Wireless Firmware loaded supports Zigbee + * and display associated informations + * @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); + + ZbStackType = p_wireless_info->StackType; /* Memorize ZB stack type */ + switch (p_wireless_info->StackType) { + case INFO_STACK_TYPE_BLE_ZIGBEE_FFD_STATIC: + APP_DBG("FW Type : STACK_TYPE_BLE_ZIGBEE_FFD_STATIC"); + break; + case INFO_STACK_TYPE_BLE_ZIGBEE_FFD_DYNAMIC: + APP_DBG("FW Type : STACK_TYPE_BLE_ZIGBEE_FFD_DYNAMIC"); + break; + case INFO_STACK_TYPE_BLE_ZIGBEE_RFD_STATIC: + APP_DBG("FW Type : STACK_TYPE_BLE_ZIGBEE_RFD_STATIC"); + break; + case INFO_STACK_TYPE_BLE_ZIGBEE_RFD_DYNAMIC: + APP_DBG("FW Type : STACK_TYPE_BLE_ZIGBEE_RFD_DYNAMIC"); + break; + 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; + } + APP_DBG("**********************************************************"); + } +} /* APP_ZIGBEE_CheckWirelessFirmwareInfo */ + +static void APP_ZIGBEE_SW1_Process(void) +{ + struct ZbApsAddrT dst; + + memset(&dst, 0, sizeof(dst)); + dst.mode = ZB_APSDE_ADDRMODE_GROUP; + dst.endpoint = SW1_ENDPOINT; + dst.nwkAddr = SW1_GROUP_ADDR; + + APP_DBG("==> SENDING TOGGLE TO GROUP 0x0001"); + if (ZbZclOnOffClientToggleReq(zigbee_app_info.onoff_client_1, &dst, NULL, NULL) != ZCL_STATUS_SUCCESS) { + APP_DBG("Error, ZbZclOnOffClientToggleReq failed (SW1_ENDPOINT)"); + } +} + +static void APP_ZIGBEE_Process_OnOff_Toggle(void) +{ + UTIL_SEQ_SetTask(1U << CFG_TASK_TOGGLE_ON_OFF, CFG_SCH_PRIO_1); +} + +static void APP_ZIGBEE_DbgNwkStatus(void) +{ + UTIL_SEQ_SetTask(1U << CFG_TASK_DBG_NWK_STATUS, CFG_SCH_PRIO_1); +} + +static void APP_ZIGBEE_OnOff_Toggle(void) +{ + struct ZbApsAddrT dst; + + memset(&dst, 0, sizeof(dst)); + dst.mode = ZB_APSDE_ADDRMODE_SHORT; + dst.endpoint = SW1_ENDPOINT; + dst.nwkAddr = 0x0; +#ifdef STRESS_TEST + uint32_t time; + + if (toggle_cnt == 0) { + time_start = HAL_GetTick(); + } +#endif + + if (ZbZclOnOffClientToggleReq(zigbee_app_info.onoff_client_1, &dst, toggle_cb, NULL) != ZCL_STATUS_SUCCESS) + { + APP_DBG("Error, ZbZclOnOffClientToggleReq failed (SW1_ENDPOINT)"); + } +#ifdef STRESS_TEST + printf("T"); + toggle_cnt++; + if ((toggle_cnt % 10) == 0) printf("\n"); + UTIL_SEQ_WaitEvt(EVENT_ON_OFF_RSP); + if ((toggle_cnt % 200) == 0) { + time = HAL_GetTick() - time_start; + APP_DBG_FULL(LOG_LEVEL_INFO, APPLI_LOG_REGION_GENERAL, "\n==> Nb TOGGLE ON/OFF sent: %d (%d Fail - %.2f%%) - %d BLE Disc - Toggle freq = %.2f ms %s", + toggle_cnt, toggle_fail, (float)(toggle_fail * 100)/(float)toggle_cnt, disc_cnt, (float)time/(float)toggle_cnt, COLOR_DEFAULT); + } +#else + toggle_cnt++; + UTIL_SEQ_WaitEvt(EVENT_ON_OFF_RSP); + APP_DBG_FULL(LOG_LEVEL_INFO, APPLI_LOG_REGION_GENERAL, "==> Nb TOGGLE ON/OFF sent: %d (%d Fail - %.2f%%) - %d BLE Disc %s", + toggle_cnt, toggle_fail, (float)(toggle_fail * 100)/(float)toggle_cnt, disc_cnt, COLOR_DEFAULT); +#endif /* STRESS_TEST */ + /* Next toggle after TOGGLE_INTERVAL */ + HW_TS_Start(Timer_ToggleOnOff_Id, (uint32_t)TOGGLE_INTERVAL); + /* Enabling Stop mode */ +#if (CFG_FULL_LOW_POWER == 1) + UTIL_LPM_SetStopMode(1U << CFG_LPM_APP, UTIL_LPM_ENABLE); +#endif /* CFG_FULL_LOW_POWER */ +} + + +static void toggle_cb(struct ZbZclCommandRspT *rsp, void *arg) +{ + if(rsp->status != ZCL_STATUS_SUCCESS) + { +#ifndef STRESS_TEST + APP_DBG("TOGGLE RSP FAIL status %d",rsp->status); +#else + printf("-"); +#endif + toggle_fail++; + } + else + { +#ifndef STRESS_TEST + APP_DBG("TOGGLE RSP SUCCESS from %#08llx",rsp->src.extAddr); +#else + printf("+"); +#endif + } + UTIL_SEQ_SetEvt(EVENT_ON_OFF_RSP); +} + +#ifdef TRACE_ZB_FULL_NNT +const char * +ZbNwk_NeighborRelationshipToStr(enum ZbNwkNeighborRelT relationship) +{ + const char *str; + + switch (relationship) { + case ZB_NWK_NEIGHBOR_REL_PARENT: + str = "Parent"; + break; + + case ZB_NWK_NEIGHBOR_REL_CHILD: + str = "Child"; + break; + + case ZB_NWK_NEIGHBOR_REL_SIBLING: + str = "Sibling"; + break; + + case ZB_NWK_NEIGHBOR_REL_NONE: + str = "None"; + break; + + case ZB_NWK_NEIGHBOR_REL_PREV_CHILD: + str = "Prev Child"; + break; + + case ZB_NWK_NEIGHBOR_REL_UNAUTH_CHILD: + str = "Unauth Child"; + break; + + case ZB_NWK_NEIGHBOR_REL_PEND_ASSOCIATE: + case ZB_NWK_NEIGHBOR_REL_PEND_ORPHAN: + str = "Pend Child"; + break; + + default: + str = "Reserved"; + break; + } /* switch */ + return (str); +} + +const char * +ZbNwk_NeighborDeviceTypeToStr(enum ZbNwkNeighborTypeT deviceType) +{ + const char *str; + switch (deviceType) { + case ZB_NWK_NEIGHBOR_TYPE_COORD: + str = "Crd"; + break; + + case ZB_NWK_NEIGHBOR_TYPE_ROUTER: + str = "Rtr"; + break; + + case ZB_NWK_NEIGHBOR_TYPE_END_DEV: + str = "End"; + break; + + case ZB_NWK_NEIGHBOR_TYPE_UNKNOWN: + default: + str = "Unk"; + break; + } /* switch */ + return (str); +} +#endif /* ifdef TRACE_ZB_FULL_NNT */ + +static void APP_ZIGBEE_Status_Nnt(void) +{ + struct ZbNwkNeighborT neighbor; + uint64_t epid; + unsigned int i; +#ifdef TRACE_ZB_FULL_NNT + unsigned int tempLen; + unsigned int nnt_index = 0; + char temp[256]; +#endif + + epid = 0; + ZbNwkGet(zigbee_app_info.zb, ZB_NWK_NIB_ID_ExtendedPanId, &epid, sizeof(uint64_t)); + +#ifdef TRACE_ZB_FULL_NNT + APP_DBG("-- ZigBee Network Neighbor Table ----------------------------------------------"); + tempLen = 0; + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-23s", "EXTADDR"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-7s", "NWKADDR"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-4s", "TYPE"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-6s", "RXIDLE"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-12s", "RELATION"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %6s", "TXFAIL"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4s", "AGE"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4s", "LQI"); + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4s", "COST"); + APP_DBG("%s", temp); +#endif + + for (i = 0;; i++) { + if (epid == 0) { + /* The NNT doesn't make much sense if we haven't joined/formed a network. + * Skip it and use the discovery table instead. */ + break; + } + if (ZbNwkGetIndex(zigbee_app_info.zb, ZB_NWK_NIB_ID_NeighborTable, &neighbor, sizeof(neighbor), i) != ZB_NWK_STATUS_SUCCESS) { + break; + } + /* Skip unused entries. */ + if (neighbor.nwkAddr == ZB_NWK_ADDR_UNDEFINED) { + continue; + } +#ifdef TRACE_ZB_FULL_NNT + nnt_index++; + + tempLen = 0; + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, "%3d ", nnt_index); + + /* EXTADDR */ + if (neighbor.extAddr) { + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, "0x%016" PRIx64 " ", neighbor.extAddr); + } + else { + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, "%-23s", " "); + } + /* NWKADDR */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " 0x%04x ", neighbor.nwkAddr); + /* TYPE */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-4s", ZbNwk_NeighborDeviceTypeToStr(neighbor.deviceType)); + /* RXIDLE */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-6s", neighbor.rxOnWhenIdle ? "True" : "False"); + /* RELATION */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %-12s", ZbNwk_NeighborRelationshipToStr(neighbor.relationship)); + /* TXFAIL */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %6d", neighbor.txFailure); + /* AGE */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4d", neighbor.age); + /* LQI */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4d", neighbor.lqi); + /* COST */ + tempLen += snprintf(&temp[tempLen], OUTPUT_LINE_MAX_LEN - tempLen, " %4d", neighbor.outgoingCost); + + APP_DBG("%s", temp); +#endif + + if (neighbor.age == 4) { /* If AGE 4 twice in a row, force update BLE connection interval */ + if (ble_sync_request) { + APP_BLE_Key_Button2_Action(); /* update BLE connection interval */ + ble_sync_request = FALSE; + } + else ble_sync_request = TRUE; + } + else { + ble_sync_request = FALSE; + } + } /* for */ + + /* Next network status after DBG_NWK_STATUS_INTERVAL */ + HW_TS_Start(Timer_DbgNwkStatus_Id, (uint32_t)DBG_NWK_STATUS_INTERVAL); +} + + +/************************************************************* + * + * WRAP FUNCTIONS + * + *************************************************************/ + +void APP_ZIGBEE_RegisterCmdBuffer(TL_CmdPacket_t *p_buffer) +{ + p_ZIGBEE_otcmdbuffer = p_buffer; +} /* APP_ZIGBEE_RegisterCmdBuffer */ + +Zigbee_Cmd_Request_t * ZIGBEE_Get_OTCmdPayloadBuffer(void) +{ + return (Zigbee_Cmd_Request_t *)p_ZIGBEE_otcmdbuffer->cmdserial.cmd.payload; +} /* ZIGBEE_Get_OTCmdPayloadBuffer */ + +Zigbee_Cmd_Request_t * ZIGBEE_Get_OTCmdRspPayloadBuffer(void) +{ + return (Zigbee_Cmd_Request_t *)((TL_EvtPacket_t *)p_ZIGBEE_otcmdbuffer)->evtserial.evt.payload; +} /* ZIGBEE_Get_OTCmdRspPayloadBuffer */ + +Zigbee_Cmd_Request_t * ZIGBEE_Get_NotificationPayloadBuffer(void) +{ + return (Zigbee_Cmd_Request_t *)(p_ZIGBEE_notif_M0_to_M4)->evtserial.evt.payload; +} /* ZIGBEE_Get_NotificationPayloadBuffer */ + +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(); +} /* ZIGBEE_CmdTransfer */ + +/** + * @brief This function is called when the M0+ acknoledge 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(); +} /* TL_ZIGBEE_CmdEvtReceived */ + +/** + * @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(); +} /* TL_ZIGBEE_NotReceived */ + +/** + * @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); +} /* Pre_ZigbeeCmdProcessing */ + +/** + * @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); +} /* Wait_Getting_Ack_From_M0 */ + +/** + * @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); +} /* Receive_Ack_From_M0 */ + +/** + * @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 + */ +void APP_ZIGBEE_ProcessNotifyM0ToM4(void) +{ + if (CptReceiveNotifyFromM0 != 0) { + /* If CptReceiveNotifyFromM0 is > 1. it means that we did not serve all the events from the radio */ + if (CptReceiveNotifyFromM0 > 1U) { + APP_ZIGBEE_Error(ERR_REC_MULTI_MSG_FROM_M0, 0); + } + else { + Zigbee_CallBackProcessing(); + } + /* Reset counter */ + CptReceiveNotifyFromM0 = 0; + } +} + +/** + * @brief Process the requests coming from the M0. + * @param + * @return + */ +void APP_ZIGBEE_ProcessRequestM0ToM4(void) +{ + if (CptReceiveRequestFromM0 != 0) { + Zigbee_M0RequestProcessing(); + CptReceiveRequestFromM0 = 0; + } +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |