Welcome to mirror list, hosted at ThFree Co, Russian Federation.

app_zigbee_demo_complete.c « App « STM32_WPAN « Zigbee_OnOff_Coord « Zigbee « Applications « P-NUCLEO-WB55.Nucleo « Projects - github.com/Flipper-Zero/STM32CubeWB.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 6b4920d0e4e20aef054d286d965e46e95b19e088 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/**
 ******************************************************************************
  * File Name          : App/app_zigbee.c
  * Description        : Zigbee Application.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 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 <assert.h>
#include "app_common.h"
#include "app_entry.h"
#include "dbg_trace.h"
#include "app_zigbee.h"
#include "shci.h"
#include "stm_logging.h"
#include "app_conf.h"
#include "stm32wbxx_core_interface_def.h"
#include "zigbee_types.h"
#include "zigbee_interface.h"
#include "stm32_seq.h"
#include "zcl/zcl.h"
#include "zcl/zcl.onoff.h"
#include "zcl/zcl.identify.h"

/* Startup Configuration -----------------------------------------------------*/
#define APP_STARTUP_DISTRIB                 0 /* Distributed Netork*/
#define APP_STARTUP_TOUCHLINK_TARGET        1 /* Touchlink Target*/
#define APP_STARTUP_TOUCHLINK_INITIATOR     2 /* Touchlink Initiator  */
#define APP_STARTUP_CENTRALIZED_COORD       3 /* Centralized Network Coordinator */
#define APP_STARTUP_CENTRALIZED_ROUTER      4 /* Centralized Network Router */

/* Choose the startup type you want from the options above */
#define APP_STARTUP_TYPE                    APP_STARTUP_CENTRALIZED_COORD

/* Spec is -40 dBm, but use -60 for ease of testing, and until we get real RSSI from STMAC. */
#define APP_TOUCHLINK_MIN_RSSI              -60

/* Private defines -----------------------------------------------------------*/
#define APP_ZIGBEE_STARTUP_FAIL_DELAY               3000U

#define SW1_ENDPOINT            17
#define SW2_ENDPOINT            18
#define TOUCHLINK_ENDPOINT      200 /* arbitrary */

#define SW1_GROUP_ADDR          0x0001
#define SW2_GROUP_ADDR          0x0002

#define SW1_FLAG                0x01
#define SW2_FLAG                0x02

#define SW1_LED                 LED_RED
#define SW2_LED                 LED_GREEN
#define SW3_LED                 LED_BLUE

#define CHANNEL                 16
/* Private function prototypes -----------------------------------------------*/

/* Private variables -----------------------------------------------*/
struct zigbee_demo_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 ZbZclClusterT *onoff_client_1;
    struct ZbZclClusterT *identify_server_1;

    struct ZbZclClusterT *onoff_server_2;
    struct ZbZclClusterT *onoff_client_2;

    uint8_t sw_flags;
};

static struct zigbee_demo_info zigbee_demo_info;

static enum ZclStatusCodeT onoff_server_attr_cb(struct ZbZclClusterT *clusterPtr, struct ZbZclAttrCbInfoT *cb);

static const struct ZbZclAttrT onoff_server_attr_list[] = {
    /* OnOff Server Attributes */
    {
        ZCL_ONOFF_ATTR_ONOFF, ZCL_DATATYPE_BOOLEAN,
        ZCL_ATTR_FLAG_REPORTABLE | ZCL_ATTR_FLAG_PERSISTABLE | ZCL_ATTR_FLAG_DEFAULTABLE | ZCL_ATTR_FLAG_CB_NOTIFY, 0,
        onoff_server_attr_cb, {0, 0}, {0, 0}
    },
};

/* Functions Definition ------------------------------------------------------*/

static enum ZclStatusCodeT onoff_server_attr_cb(struct ZbZclClusterT *clusterPtr, struct ZbZclAttrCbInfoT *cb)
{
    uint8_t endpoint;
    uint8_t attrVal;

    endpoint = ZbZclClusterGetEndpoint(clusterPtr);
    switch (cb->info->attributeId) {
        case ZCL_ONOFF_ATTR_ONOFF:
            if (endpoint == SW1_ENDPOINT) {
                if (ZbZclAttrRead(zigbee_demo_info.onoff_server_1, ZCL_ONOFF_ATTR_ONOFF, NULL, &attrVal, sizeof(attrVal), false) == ZCL_STATUS_SUCCESS) {
                    if (attrVal != 0) {
                        APP_DBG("SW1_LED ON");
                        BSP_LED_On(SW1_LED);
                    }
                    else {
                        APP_DBG("SW1_LED OFF");
                        BSP_LED_Off(SW1_LED);
                    }
                }
            }
            else if (endpoint == SW2_ENDPOINT) {
                if (ZbZclAttrRead(zigbee_demo_info.onoff_server_2, ZCL_ONOFF_ATTR_ONOFF, NULL, &attrVal, sizeof(attrVal), false) == ZCL_STATUS_SUCCESS) {
                    if (attrVal != 0) {
                        APP_DBG("SW2_LED ON");
                        BSP_LED_On(SW2_LED);
                    }
                    else {
                        APP_DBG("SW2_LED OFF");
                        BSP_LED_Off(SW2_LED);
                    }
                }
            }
            break;

        default:
            break;
    } /* switch */
    return ZCL_STATUS_SUCCESS;
} /* onoff_server_attr_notify */

static void config_endpoints(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_demo_info.zb, &req, &conf);
    assert(conf.status == ZB_STATUS_SUCCESS);
    /* OnOff Server */
    zigbee_demo_info.onoff_server_1 = ZbZclOnOffServerAlloc(zigbee_demo_info.zb, SW1_ENDPOINT, NULL);
    assert(zigbee_demo_info.onoff_server_1 != NULL);
    ZbZclAttrAppendList(zigbee_demo_info.onoff_server_1, onoff_server_attr_list, ZCL_ATTR_LIST_LEN(onoff_server_attr_list));
    ZbZclClusterEndpointRegister(zigbee_demo_info.onoff_server_1);
    /* OnOff Client */
    zigbee_demo_info.onoff_client_1 = ZbZclOnOffClientAlloc(zigbee_demo_info.zb, SW1_ENDPOINT);
    assert(zigbee_demo_info.onoff_client_1 != NULL);
    ZbZclClusterEndpointRegister(zigbee_demo_info.onoff_client_1);
    /* Identify Server for Touchlink */
    zigbee_demo_info.identify_server_1 = ZbZclIdentifyServerAlloc(zigbee_demo_info.zb, SW1_ENDPOINT, NULL);
    assert(zigbee_demo_info.identify_server_1 != NULL);

    /* Endpoint: SW2_ENDPOINT */
    req.endpoint = SW2_ENDPOINT;
    ZbZclAddEndpoint(zigbee_demo_info.zb, &req, &conf);
    assert(conf.status == ZB_STATUS_SUCCESS);
    /* OnOff Server */
    zigbee_demo_info.onoff_server_2 = ZbZclOnOffServerAlloc(zigbee_demo_info.zb, SW2_ENDPOINT, NULL);
    assert(zigbee_demo_info.onoff_server_2 != NULL);
    ZbZclAttrAppendList(zigbee_demo_info.onoff_server_2, onoff_server_attr_list, ZCL_ATTR_LIST_LEN(onoff_server_attr_list));
    ZbZclClusterEndpointRegister(zigbee_demo_info.onoff_server_2);
    /* OnOff Client */
    zigbee_demo_info.onoff_client_2 = ZbZclOnOffClientAlloc(zigbee_demo_info.zb, SW2_ENDPOINT);
    assert(zigbee_demo_info.onoff_client_2 != NULL);
    ZbZclClusterEndpointRegister(zigbee_demo_info.onoff_client_2);
} /* config_endpoints */

static void config_group_addr(void)
{
    ZbApsmeAddGroupReqT req;
    ZbApsmeAddGroupConfT conf;

    memset(&req, 0, sizeof(req));
    req.endpt = SW1_ENDPOINT;
    req.groupAddr = SW1_GROUP_ADDR;
    ZbApsmeAddGroupReq(zigbee_demo_info.zb, &req, &conf);

    req.endpt = SW2_ENDPOINT;
    req.groupAddr = SW2_GROUP_ADDR;
    ZbApsmeAddGroupReq(zigbee_demo_info.zb, &req, &conf);
} /* config_group_addr */

/**
 * @brief Zigbee Demo Task
 * @param  None
 * @retval None
 */
void APP_ZIGBEE_Demo(void)
{
    /*
     * Handle Initialization
     */
    if (!zigbee_demo_info.has_init) {
        zigbee_demo_info.sw_flags = 0;

        zigbee_demo_info.zb = ZbInit(0U, NULL, NULL);
        assert(zigbee_demo_info.zb != NULL);

        /* Create the endpoint and cluster(s) */
        config_endpoints();

        BSP_LED_Off(SW1_LED);
        BSP_LED_Off(SW2_LED);
        BSP_LED_Off(SW3_LED);

        /* Configure the joining parameters */
        zigbee_demo_info.join_status = 0x01; /* init to error status */
        zigbee_demo_info.join_delay = HAL_GetTick(); /* now */
        zigbee_demo_info.startupControl = ZbStartTypeJoin;

        /* Initialization Complete */
        zigbee_demo_info.has_init = true;
    }

    /*
     * Handle Network Joining / Forming
     */
    if ((zigbee_demo_info.join_status != ZB_STATUS_SUCCESS) && (HAL_GetTick() >= zigbee_demo_info.join_delay)) {
        struct ZbStartupT config;
        enum ZbStatusCodeT status;

        /* Configure Zigbee Logging (only need to do this once, but this is a good place to put it) */
        ZbSetLogging(zigbee_demo_info.zb, ZB_LOG_MASK_LEVEL_5, NULL);

        /* Attempt to join a zigbee network */
        ZbStartupConfigGetProDefaults(&config);

#if (APP_STARTUP_TYPE == APP_STARTUP_DISTRIB)
        APP_DBG("Network config : APP_STARTUP_DISTRIB");
        /* Set the TC address to be distributed. */
        config.security.trustCenterAddress = ZB_DISTRIBUTED_TC_ADDR;

        /* Using the Uncertified Distributed Global Key (d0:d1:d2:d3:d4:d5:d6:d7:d8:d9:da:db:dc:dd:de:df) */
        memcpy(config.security.distributedGlobalKey, sec_key_distrib_uncert, ZB_SEC_KEYSIZE);

        config.startupControl = zigbee_demo_info.startupControl;

        config.channelList.count = 1;
        config.channelList.list[0].page = 0;
        config.channelList.list[0].channelMask = 1 << CHANNEL; /* Channel in use*/

#elif (APP_STARTUP_TYPE == APP_STARTUP_TOUCHLINK_TARGET)
        APP_DBG("Network config : APP_STARTUP_TOUCHLINK_TARGET");
        config.startupControl = ZbStartTypeJoin; /* Ignored */

        /* Using the Uncertified Distributed Global Key (d0:d1:d2:d3:d4:d5:d6:d7:d8:d9:da:db:dc:dd:de:df).
         * This key can be used as an APS Link Key between Touchlink devices. */
        memcpy(config.security.distributedGlobalKey, sec_key_distrib_uncert, ZB_SEC_KEYSIZE);

        /* Use the Touchlink Certification Key (c0:c1:c2:c3:c4:c5:c6:c7:c8:c9:ca:cb:cc:cd:ce:cf).
         * This key is "mashed" with the Touchlink session data to generate the Network Key */
        ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLKey, sec_key_touchlink_cert, ZB_SEC_KEYSIZE);
        {
            /* Set the "Key Encryption Algorithm" to Certification */
            enum ZbBdbTouchlinkKeyIndexT keyIdx = TOUCHLINK_KEY_INDEX_CERTIFICATION;
            ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLKeyIndex, &keyIdx, sizeof(keyIdx));
        }

#ifdef APP_TOUCHLINK_MIN_RSSI
        /* For debugging, set the Touchlink RSSI threshold very low */
        {
            int8_t val8 = APP_TOUCHLINK_MIN_RSSI;
            status = ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLRssiMin, &val8, sizeof(val8));
        }
#endif

        /* Touchlink uses the BDB Primary and Secondary channel masks, which we'll leave as defaults. */

        /* Configure the startup to use Touchlink */
        config.bdbCommissioningMode |= BDB_COMMISSION_MODE_TOUCHLINK;
        config.touchlink.tl_endpoint = TOUCHLINK_ENDPOINT;
        config.touchlink.bind_endpoint = SW1_ENDPOINT;
        config.touchlink.flags = ZCL_TL_FLAGS_IS_TARGET;
        config.touchlink.zb_info = ZCL_TL_ZBINFO_TYPE_ROUTER;
        config.touchlink.zb_info |= ZCL_TL_ZBINFO_RX_ON_IDLE;

#elif (APP_STARTUP_TYPE == APP_STARTUP_TOUCHLINK_INITIATOR)
        APP_DBG("Network config : APP_STARTUP_TOUCHLINK_INITIATOR");
        config.startupControl = ZbStartTypeJoin; /* Ignored */

        /* Using the Uncertified Distributed Global Key (d0:d1:d2:d3:d4:d5:d6:d7:d8:d9:da:db:dc:dd:de:df).
         * This key can be used as an APS Link Key between Touchlink devices. */
        memcpy(config.security.distributedGlobalKey, sec_key_distrib_uncert, ZB_SEC_KEYSIZE);

        /* Use the Touchlink Certification Key (c0:c1:c2:c3:c4:c5:c6:c7:c8:c9:ca:cb:cc:cd:ce:cf).
         * This key is "mashed" with the Touchlink session data to generate the Network Key */
        ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLKey, sec_key_touchlink_cert, ZB_SEC_KEYSIZE);
        {
            /* Set the "Key Encryption Algorithm" to Certification */
            enum ZbBdbTouchlinkKeyIndexT keyIdx = TOUCHLINK_KEY_INDEX_CERTIFICATION;
            ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLKeyIndex, &keyIdx, sizeof(keyIdx));
        }

#ifdef APP_TOUCHLINK_MIN_RSSI
        /* For debugging, set the Touchlink RSSI threshold very low */
        {
            int8_t val8 = APP_TOUCHLINK_MIN_RSSI;
            status = ZbBdbSet(zigbee_demo_info.zb, ZB_BDB_TLRssiMin, &val8, sizeof(val8));
        }
#endif

        /* Touchlink uses the BDB Primary and Secondary channel masks, which we'll leave as defaults. */

        /* Configure the startup to use Touchlink */
        config.bdbCommissioningMode |= BDB_COMMISSION_MODE_TOUCHLINK;
        config.touchlink.tl_endpoint = TOUCHLINK_ENDPOINT;
        config.touchlink.bind_endpoint = SW1_ENDPOINT;
        config.touchlink.flags = 0x00;
        config.touchlink.zb_info = ZCL_TL_ZBINFO_TYPE_ROUTER;
        config.touchlink.zb_info |= ZCL_TL_ZBINFO_RX_ON_IDLE;

#elif (APP_STARTUP_TYPE == APP_STARTUP_CENTRALIZED_COORD)
        APP_DBG("Network config : APP_STARTUP_CENTRALIZED_COORD");
        config.startupControl = ZbStartTypeForm;

        /* 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 */

#elif (APP_STARTUP_TYPE == APP_STARTUP_CENTRALIZED_ROUTER)
        APP_DBG("Network config : APP_STARTUP_CENTRALIZED_ROUTER");
        config.startupControl = ZbStartTypeJoin;

        /* 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 = WPAN_CHANNELMASK_2400MHZ;

#else
# error "Invalid APP_STARTUP_TYPE"
#endif

        /* Using ZbStartupWait (blocking) here instead of ZbStartup, in order to demonstrate how to do
         * a blocking call on the M4. */
        status = ZbStartupWait(zigbee_demo_info.zb, &config);

        APP_DBG("ZbStartup Callback (status = 0x%02x)", status);
        zigbee_demo_info.join_status = status;

        if (status == ZB_STATUS_SUCCESS) {
            zigbee_demo_info.join_delay = 0U;
            zigbee_demo_info.init_after_join = true;
            BSP_LED_On(SW3_LED);
        }
        else {
#if (APP_STARTUP_TYPE == APP_STARTUP_DISTRIB)
            APP_DBG("Startup failed, attempting to form a network after a short delay.");
            zigbee_demo_info.startupControl = ZbStartTypeForm;
#else
            APP_DBG("Startup failed, attempting again after a short delay.");
#endif
            zigbee_demo_info.join_delay = HAL_GetTick() + APP_ZIGBEE_STARTUP_FAIL_DELAY;
        }
    }

    /*
     * Handle Application (Switches and LEDs)
     */
    if (zigbee_demo_info.join_status == ZB_STATUS_SUCCESS) {
        /* Check if we just joined */
        if (zigbee_demo_info.init_after_join) {
            zigbee_demo_info.init_after_join = false;

            /* Assign ourselves to the group addresses */
            config_group_addr();

            /* Since we're using group addressing (broadcast), shorten the broadcast timeout */
            uint32_t bcast_timeout = 3;
            ZbNwkSet(zigbee_demo_info.zb, ZB_NWK_NIB_ID_NetworkBroadcastDeliveryTime, &bcast_timeout, sizeof(bcast_timeout));
        }

        /* Check for switch flags */
        if (zigbee_demo_info.sw_flags != 0) {
            struct ZbApsAddrT dst;

            memset(&dst, 0, sizeof(dst));
            dst.mode = ZB_APSDE_ADDRMODE_GROUP;

            if ((zigbee_demo_info.sw_flags & SW1_FLAG) != 0) {
                dst.endpoint = SW1_ENDPOINT;
                dst.nwkAddr = SW1_GROUP_ADDR;

                APP_DBG("SW1 PUSHED (SENDING TOGGLE TO GROUP 0x0001)");
                if (ZbZclOnOffClientToggleReq(zigbee_demo_info.onoff_client_1, &dst, NULL, NULL) != ZB_STATUS_SUCCESS) {
                    APP_DBG("Error, ZbZclOnOffClientToggleReq failed (SW1_ENDPOINT)");
                }
                /* We will get a loopback message because we belong to the same APS Group,
                 * causing our own local LED to toggle. */
            }

            if ((zigbee_demo_info.sw_flags & SW2_FLAG) != 0) {
                dst.endpoint = SW2_ENDPOINT;
                dst.nwkAddr = SW2_GROUP_ADDR;

                APP_DBG("SW2 PUSHED (SENDING TOGGLE TO GROUP 0x0002)");
                if (ZbZclOnOffClientToggleReq(zigbee_demo_info.onoff_client_2, &dst, NULL, NULL) != ZB_STATUS_SUCCESS) {
                    APP_DBG("Error, ZbZclOnOffClientToggleReq failed (SW2_ENDPOINT)");
                }
                /* We will get a loopback message because we belong to the same APS Group,
                 * causing our own local LED to toggle. */
            }

            /* Clear the flags */
            zigbee_demo_info.sw_flags = 0;
        }

    }

    UTIL_SEQ_SetTask(1U << CFG_TASK_ZIGBEE_APP, CFG_SCH_PRIO_0);
} /* APP_ZIGBEE_Demo */

/*************************************************************
 * 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 */

/*************************************************************
 * External Interrupt Handler
 *************************************************************/

/**
  * @brief This function manage the Push button action
  * @param  GPIO_Pin : GPIO pin which has been activated
  * @retval None
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    switch (GPIO_Pin) {
        case BUTTON_SW1_PIN:
            zigbee_demo_info.sw_flags |= SW1_FLAG;
            break;

        case BUTTON_SW2_PIN:
            zigbee_demo_info.sw_flags |= SW2_FLAG;
            break;

        case BUTTON_SW3_PIN:
            APP_DBG("SW3 PUSHED (LOCAL LEDS OFF)");
            ZbZclAttrIntegerWrite(zigbee_demo_info.onoff_server_1, ZCL_ONOFF_ATTR_ONOFF, 0);
            ZbZclAttrIntegerWrite(zigbee_demo_info.onoff_server_2, ZCL_ONOFF_ATTR_ONOFF, 0);
            break;

        default:
            break;
    }
} /* HAL_GPIO_EXTI_Callback */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/