This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2640R2F: multirole satck 4.2 and stack 5.0 problems

Part Number: CC2640R2F
Other Parts Discussed in Thread: BLE-STACK

Hello,

I am using multirole with stack 4.2 (simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\blestack\multi_role) on cc2640r2f. It is working fine but at some random time it generates an assert program comes to 

void AssertHandler(uint8 assertCause, uint8 assertSubcause)


Even if don,t do anything it just keeps on scanning and then raises assert.

I have also tried with stack 5.0 (simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\multi_role).

It shows strange behavior the application just stop although it advertises but stops scanning.

When i connect Master device to it while it is Advertising. Then disconnect the master device then i don,t see any advertising and all the connections gets lost.

Why both applications are not so stable? How i can debug the issue?

Waiting for your kind help.

  • Hello MAbbas,

    It would be most helpful if you can give us a bit more information.

    1. When the application asserts, what is that assertCause and subcause?
    2. What is the callstack when the assert occurs?
    3. Can you check the ROV (see debugging chapter) when the assert occurs and ensure that the BIOS has not encountered any errors and the callstacks are sane?
    4. What, if anything have you changed in the source code of the multi_role demo?

    It sounds like the assert is related to scanning while in a connection, can you confirm this?
  • Hi Sean,

    I will let you know in two weeks once I am back to work.

    Thanks a lot.
  • Hi Sean, 

    Following are the observations for stack 4.2:

    1.  When the application asserts, assertCause = 8 and assertSubcause = 0.

    2. I have inserted the break point in AssertHandler function. The calling stack is the following:

    3. I TI-RTOS->BIOS->Scan for errors option in IAR and it shows the following at the time of assert:

    4. I have added some new functions for adding the devices to devList, enabling notifications on a specific charactristic after discovery is done, then performing write requests and getting notifications from the slave devices.

     

    In case of stack 5.0 following are the observations:

    1. The applications does not assert. (It keeps advertising but no scanning. When i connect master device (iPhone) and disconnect, then application does not advertise anymore. )

    2. Don,t have calling stack

    3. The bios does not shows any error.

    4. I have done same changes as mentioned above.

    I tested the the application with stack 5.0 for about 50 minutes. It worked fine and keeps on scanning when no device is connected.

    But when it is in connection with salve the application just stops scanning within approximately 20 minutes. The connected slave devices stay in connection with application but i am not able to perform any write to it.

    If you need any other information. Please let me know.

    I will be waiting for your kind help.

  • Hi Sean,

    Any help please ?
    I am waiting for it.
  • Hello,

    Apologies for the delay. I think we should first focus on the stack that you are wishing to use in production. Since that appears to be the BLE-Stack (3.x) I will support from there. There may be an entirely different case at hand for the BLE5-Stack.

    From your callstack, it appears that the assert is being called when Multi Role receives the HCI_BLE_HARDWARE_ERROR_EVENT_CODE.

    Please follow the steps I detailed here: e2e.ti.com/.../632880
    to get the event code reported by the stack.
  • In parallel can you post your exact modifications to the multi_role sample app that can reproduce the issue on a TI LaunchPad so that we may investigate further.
  • Hi Sean,

    Thanks for your reply. I will update you tomorrow .
  • Hello Sean,

    I followed the steps as described in reference post. I got the 

    hardwareCode = 0x83 and sometine hardwareCode = 0x85

    It means HW_FAIL_INVAILD_RF_COMMAND and HW_FAIL_UNEXPECTED_RF_STATUS.

  • Hi Sean,
    I can,t post the code publicly. Is there any way i can send the code without being public?
  • Hi MAbbas,

    Is there a minimum/stripped version of your application that can reproduce the issue that can be posted? Can you copy the necessary elements to reproduce the issue into multi_role.c and share that?
    What we need to do is be able to readily see the issue you are seeing on a TI LaunchPad.
  • Hi Sean,

    I will give an update. I am adding the code in chunks and trying to reproduce the problem.
  • Hi Sean,

    I started from scratch and keep on inserting the code to multi_role (implelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\blestack\multi_role\tirtos\iar\app) .

    I am able to reproduce the problem. To make sure i have tested it on 3 launchpads and it gives the same results.

    You can find the multi_role.c in attachments.  

    I will be waiting for your response and suggestions.

    /******************************************************************************
    
    @file       multi_role.c
    
    @brief This file contains the multi_role sample application for use
    with the CC2650 Bluetooth Low Energy Protocol Stack.
    
    Group: CMCU, SCS
    Target Device: CC2640R2
    
    ******************************************************************************
    
     Copyright (c) 2013-2017, Texas Instruments Incorporated
     All rights reserved.
    
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:
    
     *  Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
    
     *  Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
    
     *  Neither the name of Texas Instruments Incorporated nor the names of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.
    
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
    ******************************************************************************
     Release Name: simplelink_cc2640r2_sdk_1_40_00_45
     Release Date: 2017-07-20 17:16:59
    *****************************************************************************/
    
    /*********************************************************************
    * INCLUDES
    */
    #include <string.h>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Queue.h>
    #include <ti/display/Display.h>
    #include <ti/mw/lcd/LCDDogm1286.h>
    
    #if defined( USE_FPGA ) || defined( DEBUG_SW_TRACE )
    #include <driverlib/ioc.h>
    #endif // USE_FPGA | DEBUG_SW_TRACE
    
    #include <icall.h>
    #include "util.h"
    /* This Header file contains all BLE API and icall structure definition */
    #include "icall_ble_api.h"
    
    #include "devinfoservice.h"
    #include "simple_gatt_profile.h"
    #include "multi.h"
    
    #include "board_key.h"
    #include "board.h"
    #include "multi_role.h"
    
    /*********************************************************************
    * CONSTANTS
    */
    
    // Maximum number of scan responses
    // this can only be set to 15 because that is the maximum
    // amount of item actions the menu module supports
    #define DEFAULT_MAX_SCAN_RES                  7
    
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Connection parameters
    #define DEFAULT_CONN_INT                      200
    #define DEFAULT_CONN_TIMEOUT                  1000
    #define DEFAULT_CONN_LATENCY                  0
    
    // Default service discovery timer delay in ms
    #define DEFAULT_SVC_DISCOVERY_DELAY           1000
    
    // Scan parameters
    #define DEFAULT_SCAN_DURATION                 4000
    #define DEFAULT_SCAN_WIND                     80
    #define DEFAULT_SCAN_INT                      80
    
    // Discovey mode (limited, general, all)
    #define DEFAULT_DISCOVERY_MODE                DEVDISC_MODE_ALL
    
    // TRUE to use active scan
    #define DEFAULT_DISCOVERY_ACTIVE_SCAN         TRUE
    
    // TRUE to use white list during discovery
    #define DEFAULT_DISCOVERY_WHITE_LIST          FALSE
    
    // TRUE to use high scan duty cycle when creating link
    #define DEFAULT_LINK_HIGH_DUTY_CYCLE          FALSE
    
    // TRUE to use white list when creating link
    #define DEFAULT_LINK_WHITE_LIST               FALSE
    
    #define SCANNING_EVT_PERIOD                300
    
    // Task configuration
    #define MR_TASK_PRIORITY                     1
    #ifndef MR_TASK_STACK_SIZE
    #define MR_TASK_STACK_SIZE                   610
    #endif
    
    // Internal Events for RTOS application
    #define MR_ICALL_EVT                         ICALL_MSG_EVENT_ID // Event_Id_31
    #define MR_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30
    #define MR_STATE_CHANGE_EVT                  Event_Id_00
    #define MR_CHAR_CHANGE_EVT                   Event_Id_01
    #define MR_CONN_EVT_END_EVT                  Event_Id_02
    #define MR_KEY_CHANGE_EVT                    Event_Id_03
    #define MR_PAIRING_STATE_EVT                 Event_Id_04
    #define MR_PASSCODE_NEEDED_EVT               Event_Id_05
    #define MR_PERIODIC_EVT                      Event_Id_06
    #define MR_SCANNING_EVT                      Event_Id_07
    
    #define MR_ALL_EVENTS                        (MR_ICALL_EVT           | \
                                                 MR_QUEUE_EVT            | \
                                                 MR_STATE_CHANGE_EVT     | \
                                                 MR_CHAR_CHANGE_EVT      | \
                                                 MR_CONN_EVT_END_EVT     | \
                                                 MR_KEY_CHANGE_EVT       | \
                                                 MR_PAIRING_STATE_EVT    | \
                                                 MR_PERIODIC_EVT         | \
                                                 MR_SCANNING_EVT)
    
    
    // address string length is an ascii character for each digit +
    // an initial 0x + an ending null character
    #define B_STR_ADDR_LEN       ((B_ADDR_LEN*2) + 3)
    
    // How often to perform periodic event (in msec)
    #define MR_PERIODIC_EVT_PERIOD               5000
    
    /*********************************************************************
    * TYPEDEFS
    */
    
    // App event passed from profiles.
    typedef struct
    {
      uint16_t event;  // event type
      uint8_t *pData;  // event data pointer
    } mrEvt_t;
    
    // pairing callback event
    typedef struct
    {
      uint16_t connectionHandle; // connection Handle
      uint8_t state;             // state returned from GAPBondMgr
      uint8_t status;            // status of state
    } gapPairStateEvent_t;
    
    // Discovery states
    typedef enum {
      BLE_DISC_STATE_IDLE,                // Idle
      BLE_DISC_STATE_MTU,                 // Exchange ATT MTU size
      BLE_DISC_STATE_SVC,                 // Service discovery
      BLE_DISC_STATE_CHAR                 // Characteristic discovery
    } discState_t;
    
    // discovery information
    typedef struct
    {
      discState_t discState;   // discovery state
      uint16_t svcStartHdl;    // service start handle
      uint16_t svcEndHdl;      // service end handle
      uint16_t charHdl;        // characteristic handle
    } discInfo_t;
    
    // entry to map index to connection handle and store address string for menu module
    typedef struct
    {
      uint16_t connHandle;              // connection handle of an active connection
    } connHandleMapEntry_t;
    
    /*********************************************************************
    * GLOBAL VARIABLES
    */
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    
    /*********************************************************************
    * LOCAL VARIABLES
    */
    
    /*********************************************************************
    * LOCAL VARIABLES
    */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Event globally used to post local events and pend on system and
    // local events.
    static ICall_SyncHandle syncEvent;
    
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock;
    
    // Clock instance for scanning
    static Clock_Struct periodicScanClock;
    
    // Queue object used for app messages
    static Queue_Struct appMsg;
    static Queue_Handle appMsgQueue;
    
    // Task configuration
    Task_Struct mrTask;
    Char mrTaskStack[MR_TASK_STACK_SIZE];
    
    static uint8_t scanRspData[] =
    {
      // complete name
      11,   // length of this data
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'M', 'u', 'l', 't', 'i', ' ', 'R', 'o', 'l', 'e',
    
    
    
      // Tx power level
      0x02,   // length of this data
      GAP_ADTYPE_POWER_LEVEL,
      0       // 0dBm
    };
    
    // GAP - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) instead of general
      // discoverable mode (advertises indefinitely)
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // service UUID, to notify central devices what services are included
      // in this peripheral
      0x03,   // length of this data
      GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
      LO_UINT16(SIMPLEPROFILE_SERV_UUID),
      HI_UINT16(SIMPLEPROFILE_SERV_UUID)
    };
    
    // pointer to allocate the connection handle map
    static connHandleMapEntry_t *connHandleMap;
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Multi Role :)";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    // Pointer to per connection discovery info
    discInfo_t *discInfo;
    
    // Maximim PDU size (default = 27 octets)
    static uint16 maxPduSize;
    
    // Scanning started flag
    static bool scanningStarted = FALSE;
    
    // Connecting flag
    static uint8_t connecting = FALSE;
    
    // Value to write
    static uint8_t charVal = 0;
    
    // Value read/write toggle
    static bool doWrite = FALSE;
    
    // Dummy parameters to use for connection updates
    gapRole_updateConnParams_t updateParams =
    {
      .connHandle = INVALID_CONNHANDLE,
      .minConnInterval = 80,
      .maxConnInterval = 150,
      .slaveLatency = 0,
      .timeoutMultiplier = 200
    };
    
    // Connection index for mapping connection handles
    static uint16_t connIndex = INVALID_CONNHANDLE;
    
    // Maximum number of connected devices
    static uint8_t maxNumBleConns = MAX_NUM_BLE_CONNS;
    
    /*********************************************************************
    * LOCAL FUNCTIONS
    */
    static void multi_role_init( void );
    static void multi_role_taskFxn(UArg a0, UArg a1);
    static uint8_t multi_role_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t multi_role_processGATTMsg(gattMsgEvent_t *pMsg);
    static void multi_role_processAppMsg(mrEvt_t *pMsg);
    static void multi_role_processCharValueChangeEvt(uint8_t paramID);
    static void multi_role_processRoleEvent(gapMultiRoleEvent_t *pEvent);
    static void multi_role_sendAttRsp(void);
    static void multi_role_freeAttRsp(uint8_t status);
    static void multi_role_charValueChangeCB(uint8_t paramID);
    static uint8_t multi_role_enqueueMsg(uint16_t event, uint8_t *pData);
    static void multi_role_startDiscovery(uint16_t connHandle);
    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg);
    static uint8_t multi_role_eventCB(gapMultiRoleEvent_t *pEvent);
    static void multi_role_paramUpdateDecisionCB(gapUpdateLinkParamReq_t *pReq,
                                                 gapUpdateLinkParamReqReply_t *pRsp);
    static void multi_role_sendAttRsp(void);
    static void multi_role_freeAttRsp(uint8_t status);
    static uint16_t multi_role_mapConnHandleToIndex(uint16_t connHandle);
    static uint8_t multi_role_addMappingEntry(uint16_t connHandle, uint8_t *addr);
    static void multi_role_performPeriodicTask(void);
    static void multi_role_clockHandler(UArg arg);
    
    /*********************************************************************
     * EXTERN FUNCTIONS
    */
    extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
    
    /*********************************************************************
    * PROFILE CALLBACKS
    */
    
    // GAP Role Callbacks
    static gapRolesCBs_t multi_role_gapRoleCBs =
    {
      multi_role_eventCB,                   // Events to be handled by the app are passed through the GAP Role here
      multi_role_paramUpdateDecisionCB      // Callback for application to decide whether to accept a param update
    };
    
    // Simple GATT Profile Callbacks
    static simpleProfileCBs_t multi_role_simpleProfileCBs =
    {
      multi_role_charValueChangeCB // Characteristic value change callback
    };
    
    
    /*********************************************************************
    * PUBLIC FUNCTIONS
    */
    
    /*********************************************************************
    * @fn      multi_role_createTask
    *
    * @brief   Task creation function for multi_role.
    *
    * @param   None.
    *
    * @return  None.
    */
    void multi_role_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = mrTaskStack;
      taskParams.stackSize = MR_TASK_STACK_SIZE;
      taskParams.priority = MR_TASK_PRIORITY;
    
      Task_construct(&mrTask, multi_role_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
    * @fn      multi_role_init
    *
    * @brief   Called during initialization and contains application
    *          specific initialization (ie. hardware initialization/setup,
    *          table initialization, power up notification, etc), and
    *          profile initialization/setup.
    *
    * @param   None.
    *
    * @return  None.
    */
    static void multi_role_init(void)
    {
      // ******************************************************************
      // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages.
      ICall_registerApp(&selfEntity, &syncEvent);
    
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueue = Util_constructQueue(&appMsg);
    
      // Create one-shot clocks for internal periodic events.
      Util_constructClock(&periodicClock, multi_role_clockHandler,
                          MR_PERIODIC_EVT_PERIOD, 0, false, MR_PERIODIC_EVT);
      
          // Create one-shot clocks for scanning for the slave devices.
      Util_constructClock(&periodicScanClock, multi_role_clockHandler,
                          SCANNING_EVT_PERIOD, 0, true, MR_SCANNING_EVT);
    
      /**************Init Menu*****************************/
    
    
      // Setup the GAP
      {
        // Set advertising interval the same for all scenarios
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
        GAP_SetParamValue(TGAP_CONN_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_CONN_ADV_INT_MAX, advInt);
    
        // Set scan duration
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION);
    
        // Scan interval and window the same for all scenarios
        GAP_SetParamValue(TGAP_CONN_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_LIM_DISC_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_LIM_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_CONN_EST_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_SCAN_WIND, DEFAULT_SCAN_WIND);
    
        // Set connection parameters
        GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_CONN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_INT_MAX, DEFAULT_CONN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_SUPERV_TIMEOUT, DEFAULT_CONN_TIMEOUT);
        GAP_SetParamValue(TGAP_CONN_EST_LATENCY, DEFAULT_CONN_LATENCY);
    
        // Register to receive GAP and HCI messages
        GAP_RegisterForMsgs(selfEntity);
      }
    
      // Setup the GAP Role Profile
      {
        /*--------PERIPHERAL-------------*/
        uint8_t initialAdvertEnable = TRUE;
        uint16_t advertOffTime = 0;
    
        // device starts advertising upon initialization
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                             &initialAdvertEnable, NULL);
    
        // By setting this to zero, the device will go into the waiting state after
        // being discoverable for 30.72 second, and will not being advertising again
        // until the enabler is set back to TRUE
        GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                             &advertOffTime, NULL);
    
        // Set scan response data
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData),
                             scanRspData, NULL);
    
        // Set advertising data
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData, NULL);
    
        // set max amount of scan responses
        uint8_t scanRes = DEFAULT_MAX_SCAN_RES;
    
        // Set the max amount of scan responses
        GAPRole_SetParameter(GAPROLE_MAX_SCAN_RES, sizeof(uint8_t),
                             &scanRes, NULL);
    
        // Start the GAPRole and negotiate max number of connections
        VOID GAPRole_StartDevice(&multi_role_gapRoleCBs, &maxNumBleConns);
    
        // Allocate memory for index to connection handle map
        if (connHandleMap = ICall_malloc(sizeof(connHandleMapEntry_t) * maxNumBleConns))
        {
          // Init index to connection handle map
          for (uint8_t i = 0; i < maxNumBleConns; i++)
          {
            connHandleMap[i].connHandle = INVALID_CONNHANDLE;
          }
        }
    
        // Allocate memory for per connection discovery information
        if (discInfo = ICall_malloc(sizeof(discInfo_t) * maxNumBleConns))
        {
          // Init index to connection handle map to 0's
          for (uint8_t i = 0; i < maxNumBleConns; i++)
          {
            discInfo[i].charHdl = 0;
            discInfo[i].discState = BLE_DISC_STATE_IDLE;
            discInfo[i].svcEndHdl = 0;
            discInfo[i].svcStartHdl = 0;
          }
        }
      }
    
      // GATT
      {
        // Set the GAP Characteristics
        GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
        // Initialize GATT Server Services
        GGS_AddService(GATT_ALL_SERVICES);           // GAP
        GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
        DevInfo_AddService();                        // Device Information Service
        SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
    
        // Setup Profile Characteristic Values
        {
          uint8_t charValue1 = 1;
          uint8_t charValue2 = 2;
          uint8_t charValue3 = 3;
          uint8_t charValue4 = 4;
          uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };
    
          SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, sizeof(uint8_t),
                                     &charValue1);
          SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t),
                                     &charValue2);
          SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t),
                                     &charValue3);
          SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                                     &charValue4);
          SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN,
                                     charValue5);
        }
    
        // Register callback with SimpleGATTprofile
        SimpleProfile_RegisterAppCBs(&multi_role_simpleProfileCBs);
    
        /*-----------------CLIENT------------------*/
        // Initialize GATT Client
        VOID GATT_InitClient();
    
        // Register for GATT local events and ATT Responses pending for transmission
        GATT_RegisterForMsgs(selfEntity);
    
        // Register to receive incoming ATT Indications/Notifications
        GATT_RegisterForInd(selfEntity);
      }
    
    #if !defined (USE_LL_CONN_PARAM_UPDATE)
      // Get the currently set local supported LE features
      // The HCI will generate an HCI event that will get received in the main
      // loop
      HCI_LE_ReadLocalSupportedFeaturesCmd();
    #endif // !defined (USE_LL_CONN_PARAM_UPDATE)
    
    }
    
    /*********************************************************************
    * @fn      multi_role_taskFxn
    *
    * @brief   Application task entry point for the multi_role.
    *
    * @param   a0, a1 - not used.
    *
    * @return  None.
    */
    static void multi_role_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      multi_role_init();
    
      // Application main loop
      for (;;)
      {
        uint32_t events;
    
        // Waits for an event to be posted associated with the calling thread.
        // Note that an event associated with a thread is posted when a
        // message is queued to the message receive queue of the thread
        events = Event_pend(syncEvent, Event_Id_NONE, MR_ALL_EVENTS,
                            ICALL_TIMEOUT_FOREVER);
    
        if (events)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          if (ICall_fetchServiceMsg(&src, &dest,
                                    (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
          {
            uint8_t safeToDealloc = TRUE;
    
            if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
            {
              ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
    
              // Check for BLE stack events first
              if (pEvt->signature == 0xffff)
              {
                if (pEvt->event_flag & MR_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  multi_role_sendAttRsp();
                }
              }
              else
              {
                // Process inter-task message
                safeToDealloc = multi_role_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // If RTOS queue is not empty, process app message.
          if (events & MR_QUEUE_EVT)
          {
            while (!Queue_empty(appMsgQueue))
            {
              mrEvt_t *pMsg = (mrEvt_t *)Util_dequeueMsg(appMsgQueue);
              if (pMsg)
              {
                // Process message.
                multi_role_processAppMsg(pMsg);
    
                // Free the space from the message.
                ICall_free(pMsg);
              }
            }
          }
    
          if (events & MR_PERIODIC_EVT)
          {
            Util_startClock(&periodicClock);
    
            // Perform periodic application task
            multi_role_performPeriodicTask();
          }
          
          if(events & MR_SCANNING_EVT)
          {
            mr_doScan(0);
            Util_restartClock(&periodicScanClock,SCANNING_EVT_PERIOD);
          }
    
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processStackMsg
    *
    * @brief   Process an incoming stack message.
    *
    * @param   pMsg - message to process
    *
    * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
    */
    uint8_t hardErrCode = 0xFF;
    static uint8_t multi_role_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = multi_role_processGATTMsg((gattMsgEvent_t *)pMsg);
          break;
    
        case HCI_GAP_EVENT_EVENT:
          {
            // Process HCI message
            switch(pMsg->status)
            {
              case HCI_COMMAND_COMPLETE_EVENT_CODE:
                // Process HCI Command Complete Event
                {
      #if !defined (USE_LL_CONN_PARAM_UPDATE)
    
                  // This code will disable the use of the LL_CONNECTION_PARAM_REQ
                  // control procedure (for connection parameter updates, the
                  // L2CAP Connection Parameter Update procedure will be used
                  // instead). To re-enable the LL_CONNECTION_PARAM_REQ control
                  // procedures, define the symbol USE_LL_CONN_PARAM_UPDATE
    
                  // Parse Command Complete Event for opcode and status
                  hciEvt_CmdComplete_t* command_complete = (hciEvt_CmdComplete_t*) pMsg;
                  uint8_t   pktStatus = command_complete->pReturnParam[0];
    
                  //find which command this command complete is for
                  switch (command_complete->cmdOpcode)
                  {
                    case HCI_LE_READ_LOCAL_SUPPORTED_FEATURES:
                      {
                        if (pktStatus == SUCCESS)
                        {
                          uint8_t featSet[8];
    
                          // get current feature set from received event (bits 1-9 of
                          // the returned data
                          memcpy( featSet, &command_complete->pReturnParam[1], 8 );
    
                          // Clear bit 1 of byte 0 of feature set to disable LL
                          // Connection Parameter Updates
                          CLR_FEATURE_FLAG( featSet[0], LL_FEATURE_CONN_PARAMS_REQ );
    
                          // Update controller with modified features
                          HCI_EXT_SetLocalSupportedFeaturesCmd( featSet );
                        }
                      }
                      break;
    
                    default:
                      //do nothing
                      break;
                  }
      #endif // !defined (USE_LL_CONN_PARAM_UPDATE)
    
                }
                break;
    
              case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
              {
                hciEvt_HardwareError_t *hardCode =(hciEvt_HardwareError_t*)pMsg;
                hardErrCode = hardCode->hardwareCode;
                AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
              }
                break;
    
              default:
                break;
            }
          }
          break;
    
        case GAP_MSG_EVENT:
          multi_role_processRoleEvent((gapMultiRoleEvent_t *)pMsg);
          break;
    
        default:
          // Do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
    * @fn      multi_role_processGATTMsg
    *
    * @brief   Process GATT messages and events.
    *
    * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
    */
    static uint8_t multi_role_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       MR_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          multi_role_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Display the opcode of the message that caused the violation.
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
    
      }
    
      // Messages from GATT server
      if (linkDB_NumActive() > 0)
      {
        // Find index from connection handle
        connIndex = multi_role_mapConnHandleToIndex(pMsg->connHandle);
        if ((pMsg->method == ATT_READ_RSP)   ||
            ((pMsg->method == ATT_ERROR_RSP) &&
             (pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
        {
          if (pMsg->method == ATT_ERROR_RSP)
          {
          }
          else
          {
          }
    
        }
        else if ((pMsg->method == ATT_WRITE_RSP)  ||
                 ((pMsg->method == ATT_ERROR_RSP) &&
                  (pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
        {
    
          if (pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP)
          {
    
          }
          else
          {
            // After a succesful write, display the value that was written and
            // increment value
    
          }
        }
        else if (discInfo[connIndex].discState != BLE_DISC_STATE_IDLE)
        {
          multi_role_processGATTDiscEvent(pMsg);
        }
      } // Else - in case a GATT message came after a connection has dropped, ignore it.
    
      // Free message payload. Needed only for ATT Protocol messages
      GATT_bm_free(&pMsg->msg, pMsg->method);
    
      // It's safe to free the incoming message
      return (TRUE);
    }
    
    /*********************************************************************
    * @fn      multi_role_sendAttRsp
    *
    * @brief   Send a pending ATT response message.
    *
    * @param   none
    *
    * @return  none
    */
    static void multi_role_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          multi_role_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
    
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_freeAttRsp
    *
    * @brief   Free ATT response message.
    *
    * @param   status - response transmit status
    *
    * @return  none
    */
    static void multi_role_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
    
        }
        else
        {
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processAppMsg
    *
    * @brief   Process an incoming callback from a profile.
    *
    * @param   pMsg - message to process
    *
    * @return  None.
    */
    static void multi_role_processAppMsg(mrEvt_t *pMsg)
    {
      switch (pMsg->event)
      {
      case MR_STATE_CHANGE_EVT:
        multi_role_processStackMsg((ICall_Hdr *)pMsg->pData);
        // Free the stack message
        ICall_freeMsg(pMsg->pData);
        break;
    
      case MR_CHAR_CHANGE_EVT:
        multi_role_processCharValueChangeEvt(*(pMsg->pData));
        // Free the app data
        ICall_free(pMsg->pData);
        break;
    
      case MR_KEY_CHANGE_EVT:
        // Free the app data
        ICall_free(pMsg->pData);
        break;
    
      case MR_PAIRING_STATE_EVT:
        // Free the app data
        ICall_free(pMsg->pData);
        break;
    
      case MR_PASSCODE_NEEDED_EVT:
        // Free the app data
        ICall_free(pMsg->pData);
        break;
    
      default:
        // Do nothing.
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_eventCB
    *
    * @brief   Multi GAPRole event callback function.
    *
    * @param   pEvent - pointer to event structure
    *
    * @return  TRUE if safe to deallocate event message, FALSE otherwise.
    */
    static uint8_t multi_role_eventCB(gapMultiRoleEvent_t *pEvent)
    {
      // Forward the role event to the application
      if (multi_role_enqueueMsg(MR_STATE_CHANGE_EVT, (uint8_t *)pEvent))
      {
        // App will process and free the event
        return FALSE;
      }
    
      // Caller should free the event
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      multi_role_paramUpdateDecisionCB
    *
    * @brief   Callback for application to decide whether or not to accept
    *          a parameter update request and, if accepted, what parameters
    *          to use
    *
    * @param   pReq - pointer to param update request
    * @param   pRsp - pointer to param update response
    *
    * @return  none
    */
    static void multi_role_paramUpdateDecisionCB(gapUpdateLinkParamReq_t *pReq,
                                                 gapUpdateLinkParamReqReply_t *pRsp)
    {
      // Make some decision based on desired parameters. Here is an example
      // where only parameter update requests with 0 slave latency are accepted
      if (pReq->connLatency == 0)
      {
        // Accept and respond with remote's desired parameters
        pRsp->accepted = TRUE;
        pRsp->connLatency = pReq->connLatency;
        pRsp->connTimeout = pReq->connTimeout;
        pRsp->intervalMax = pReq->intervalMax;
        pRsp->intervalMin = pReq->intervalMin;
      }
    
      // Don't accept param update requests with slave latency other than 0
      else
      {
        pRsp->accepted = FALSE;
      }
    }
    /*********************************************************************
    * @fn      multi_role_processRoleEvent
    *
    * @brief   Multi role event processing function.
    *
    * @param   pEvent - pointer to event structure
    *
    * @return  none
    */
    static void multi_role_processRoleEvent(gapMultiRoleEvent_t *pEvent)
    {
      switch (pEvent->gap.opcode)
      {
        // GAPRole started
        case GAP_DEVICE_INIT_DONE_EVENT:
        {
          // Store max pdu size
          maxPduSize = pEvent->initDone.dataPktLen;
    
          // Set device info characteristic
          DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, pEvent->initDone.devAddr);
        }
        break;
    
        // Advertising started
        case GAP_MAKE_DISCOVERABLE_DONE_EVENT:
        {
    
        }
        break;
    
        // Advertising ended
        case GAP_END_DISCOVERABLE_DONE_EVENT:
        {
          // Display advertising info depending on whether there are any connections
          if (linkDB_NumActive() < maxNumBleConns)
          {
    
          }
          else
          {
    
          }
        }
        break;
    
        // A discovered device report
        case GAP_DEVICE_INFO_EVENT:
        {
          // Do nothing. scan results are handled at the end of scanning
            if(pEvent->deviceInfo.eventType == GAP_ADRPT_ADV_IND){    
              uint8_t firstByte = pEvent->deviceInfo.pEvtData[11];
              uint8_t secondByte = pEvent->deviceInfo.pEvtData[12];
              if(firstByte == 0xFF && secondByte == 0xFF )
              {
               // foundDevice(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
              }  
            }
        }
        break;
    
        // End of discovery report
        case GAP_DEVICE_DISCOVERY_EVENT:
        {
    
          // Discovery complete
          scanningStarted = FALSE;
        }
        break;
    
        // Connection has been established
        case GAP_LINK_ESTABLISHED_EVENT:
        {
          // If succesfully established
          if (pEvent->gap.hdr.status == SUCCESS)
          {
            // Clear connecting flag
            connecting = FALSE;
    
            // Add index-to-connHandle mapping entry and update menus
            uint8_t index = multi_role_addMappingEntry(pEvent->linkCmpl.connectionHandle, pEvent->linkCmpl.devAddr);
    
            //turn off advertising if no available links
            if (linkDB_NumActive() >= maxNumBleConns)
            {
              uint8_t advertEnabled = FALSE;
              GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnabled, NULL);
            }
    
    
            // Start service discovery
            multi_role_startDiscovery(pEvent->linkCmpl.connectionHandle);
    
            // Start periodic clock if this is the first connection
            if (linkDB_NumActive() == 1)
            {
              Util_startClock(&periodicClock);
            }
          }
          // If the connection was not successfully established
          else
          {
    
          }
        }
        break;
    
        // Connection has been terminated
        case GAP_LINK_TERMINATED_EVENT:
        {
          // read current num active so that this doesn't change before this event is processed
          uint8_t currentNumActive = linkDB_NumActive();
    
          // Find index from connection handle
          connIndex = multi_role_mapConnHandleToIndex(pEvent->linkTerminate.connectionHandle);
    
          // Check to prevent buffer overrun
          if (connIndex < maxNumBleConns)
          {
            // Clear screen, reset discovery info, and return to main menu
            connHandleMap[connIndex].connHandle = INVALID_CONNHANDLE;
    
            // Reset discovery info
            discInfo[connIndex].discState = BLE_DISC_STATE_IDLE;
            discInfo[connIndex].charHdl = 0;
    
            // If there aren't any active connections
            if (currentNumActive == 0)
            {
    
    
              // Stop periodic clock
              Util_stopClock(&periodicClock);
            }
    
            // Clear screen
    
            // If it is possible to advertise again
            if (currentNumActive == (maxNumBleConns-1))
            {
    
            }
          }
        }
        break;
    
        // A parameter update has occurred
        case GAP_LINK_PARAM_UPDATE_EVENT:
        {
        }
        break;
    
      default:
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_charValueChangeCB
    *
    * @brief   Callback from Simple Profile indicating a characteristic
    *          value change.
    *
    * @param   paramID - parameter ID of the value that was changed.
    *
    * @return  None.
    */
    static void multi_role_charValueChangeCB(uint8_t paramID)
    {
      uint8_t *pData;
    
      // Allocate space for the event data.
      if ((pData = ICall_malloc(sizeof(uint8_t))))
      {
        *pData = paramID;
    
        // Queue the event.
        multi_role_enqueueMsg(MR_CHAR_CHANGE_EVT, pData);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processCharValueChangeEvt
    *
    * @brief   Process a pending Simple Profile characteristic value change
    *          event.
    *
    * @param   paramID - parameter ID of the value that was changed.
    *
    * @return  None.
    */
    static void multi_role_processCharValueChangeEvt(uint8_t paramID)
    {
      uint8_t newValue;
    
      // Print new value depending on which characteristic was updated
      switch(paramID)
      {
      case SIMPLEPROFILE_CHAR1:
        // Get new value
        SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
    
        break;
    
      case SIMPLEPROFILE_CHAR3:
        // Get new value
        SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
    
    
        break;
    
      default:
        // Should not reach here!
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_enqueueMsg
    *
    * @brief   Creates a message and puts the message in RTOS queue.
    *
    * @param   event - message event.
    * @param   pData - pointer to data to be queued
    *
    * @return  None.
    */
    static uint8_t multi_role_enqueueMsg(uint16_t event, uint8_t *pData)
    {
      // Allocate space for the message
      mrEvt_t *pMsg = ICall_malloc(sizeof(mrEvt_t));
    
      // If sucessfully allocated
      if (pMsg)
      {
        // Fill up message
        pMsg->event = event;
        pMsg->pData = pData;
    
        // Enqueue the message.
        return Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
      }
    
      return FALSE;
    }
    
    
    /*********************************************************************
    * @fn      multi_role_startDiscovery
    *
    * @brief   Start service discovery.
    *
    * @param   connHandle - connection handle
    *
    * @return  none
    */
    static void multi_role_startDiscovery(uint16_t connHandle)
    {
      // Exchange MTU request
      attExchangeMTUReq_t req;
    
      // Map connection handle to index
      connIndex = multi_role_mapConnHandleToIndex(connHandle);
    
      // Check to prevent buffer overrun
      if (connIndex < maxNumBleConns)
      {
        // Update discovery state of this connection
        discInfo[connIndex].discState= BLE_DISC_STATE_MTU;
    
        // Initialize cached handles
        discInfo[connIndex].svcStartHdl = discInfo[connIndex].svcEndHdl = 0;
      }
    
      // Discover GATT Server's Rx MTU size
      req.clientRxMTU = maxPduSize - L2CAP_HDR_SIZE;
    
      // ATT MTU size should be set to the minimum of the Client Rx MTU
      // and Server Rx MTU values
      VOID GATT_ExchangeMTU(connHandle, &req, selfEntity);
    }
    
    /*********************************************************************
    * @fn      multi_role_processGATTDiscEvent
    *
    * @brief   Process GATT discovery event
    *
    * @param   pMsg - pointer to discovery event stack message
    *
    * @return  none
    */
    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    {
      // Map connection handle to index
      connIndex = multi_role_mapConnHandleToIndex(pMsg->connHandle);
      // Check to prevent buffer overrun
      if (connIndex < maxNumBleConns)
      {
        //MTU update
        if (pMsg->method == ATT_MTU_UPDATED_EVENT)
        {
          // MTU size updated
        }
        // If we've updated the MTU size
        else if (discInfo[connIndex].discState == BLE_DISC_STATE_MTU)
        {
          // MTU size response received, discover simple BLE service
          if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
          {
            uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
            HI_UINT16(SIMPLEPROFILE_SERV_UUID) };
    
            // Advance state
            discInfo[connIndex].discState= BLE_DISC_STATE_SVC;
    
            // Discovery of simple BLE service
            VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid, ATT_BT_UUID_SIZE,
                                               selfEntity);
          }
        }
        // If we're performing service discovery
        else if (discInfo[connIndex].discState == BLE_DISC_STATE_SVC)
        {
          // Service found, store handles
          if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
              pMsg->msg.findByTypeValueRsp.numInfo > 0)
          {
            discInfo[connIndex].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
            discInfo[connIndex].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
          }
    
          // If procedure is complete
          if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
               (pMsg->hdr.status == bleProcedureComplete))  ||
              (pMsg->method == ATT_ERROR_RSP))
          {
            // If we've discovered the service
            if (discInfo[connIndex].svcStartHdl != 0)
            {
              attReadByTypeReq_t req;
    
              // Discover characteristic
              discInfo[connIndex].discState = BLE_DISC_STATE_CHAR;
              req.startHandle = discInfo[connIndex].svcStartHdl;
              req.endHandle = discInfo[connIndex].svcEndHdl;
              req.type.len = ATT_BT_UUID_SIZE;
              req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
              req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
    
              // Send characteristic discovery request
              VOID GATT_ReadUsingCharUUID(pMsg->connHandle, &req, selfEntity);
            }
          }
        }
        // If we're discovering characteristics
        else if (discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
        {
          // Characteristic found
          if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
              (pMsg->msg.readByTypeRsp.numPairs > 0))
          {
            // Store handle
            discInfo[connIndex].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                                                       pMsg->msg.readByTypeRsp.pDataList[1]);
    
          }
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_mapConnHandleToIndex
    *
    * @brief   Translates connection handle to index
    *
    * @param   connHandle - the connection handle
    *
    * @return  index or INVALID_CONNHANDLE if connHandle isn't found
    */
    static uint16_t multi_role_mapConnHandleToIndex(uint16_t connHandle)
    {
      uint16_t index;
      // Loop through connection
      for (index = 0; index < maxNumBleConns; index ++)
      {
        // If matching connection handle found
        if (connHandleMap[index].connHandle == connHandle)
        {
          return index;
        }
      }
      // Not found if we got here
      return INVALID_CONNHANDLE;
    }
    
    
    /*********************************************************************
     * @fn      multi_role_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     */
    static void multi_role_clockHandler(UArg arg)
    {
      // Wake up the application.
      Event_post(syncEvent, arg);
    }
    
    /*********************************************************************
     * @brief   Perform a periodic application task to demonstrate notification
     *          capabilities of simpleGATTProfile. This function gets called
     *          every five seconds (MR_PERIODIC_EVT_PERIOD). In this example,
     *          the value of the third characteristic in the SimpleGATTProfile
     *          service is retrieved from the profile, and then copied into the
     *          value of the the fourth characteristic.
     *
     */
    static void multi_role_performPeriodicTask(void)
    {
      uint8_t valueToCopy;
    
      // Call to retrieve the value of the third characteristic in the profile
      if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS)
      {
        // Call to set that value of the fourth characteristic in the profile.
        // Note that if notifications of the fourth characteristic have been
        // enabled by a GATT client device, then a notification will be sent
        // every time this function is called. Also note that this will
        // send a notification to each connected device.
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),
                                   &valueToCopy);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_addMappingEntry
    *
    * @brief   add a new connection to the index-to-connHandle map
    *
    * @param   connHandle - the connection handle
    *
    * @param   addr - pointer to device address
    *
    * @return  index of connection handle
    */
    static uint8_t multi_role_addMappingEntry(uint16_t connHandle, uint8_t *addr)
    {
      uint16_t index;
      // Loop though connections
      for (index = 0; index < maxNumBleConns; index++)
      {
        // If there is an open connection
        if (connHandleMap[index].connHandle == INVALID_CONNHANDLE)
        {
          // Store mapping
          connHandleMap[index].connHandle = connHandle;
    
          // Convert address to string
          uint8_t *pAddr = (uint8_t *) Util_convertBdAddr2Str(addr);
    
          return index;
        }
      }
      // No room if we get here
      return bleNoResources;
    }
    
    /*********************************************************************
    * @fn      mr_doScan
    *
    * @brief   Respond to user input to start scanning
    *
    * @param   index - not used
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doScan(uint8_t index)
    {
      (void) index;
    
      // If we can connect to another device
      if (linkDB_NumActive() < maxNumBleConns)
      {
        // If we're not already scanning
        if (!scanningStarted)
        {
          // Set scannin started flag
          scanningStarted = TRUE;
    
          // Start scanning
          GAPRole_StartDiscovery(DEFAULT_DISCOVERY_MODE,
                                 DEFAULT_DISCOVERY_ACTIVE_SCAN, DEFAULT_DISCOVERY_WHITE_LIST);
        }
        // We're already scanning...so cancel
        else
        {
          // Cancel scanning
          GAPRole_CancelDiscovery();
    
    
          // Clear scanning started flag
          scanningStarted = FALSE;
        }
      }
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      mr_doConnect
    *
    * @brief   Respond to user input to form a connection
    *
    * @param   index - index as selected from the mrMenuConnect
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doConnect(uint8_t addrType, uint8_t *addr)
    {
      // If already connecting...cancel
      if (connecting == TRUE)
      {
        // Cancel connection request
        GAPRole_TerminateConnection(GAP_CONNHANDLE_INIT);
    
        // Clear connecting flag
        connecting = FALSE;
      }
      // If attempting to connect
      else
      {
        // Connect to current device in scan result
        GAPRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
                              DEFAULT_LINK_WHITE_LIST,
                              addrType, addr);
    
        // Set connecting state flag
        connecting = TRUE;
      }
    
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      mr_doGattRw
    *
    * @brief   Respond to user input to do a GATT read or write
    *
    * @param   index - index as selected from the mrMenuGattRw
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doGattRw(uint8_t index)
    {
      bStatus_t status = FAILURE;
      // If characteristic has been discovered
      if (discInfo[index].charHdl != 0)
      {
        // Do a read / write as long as no other read or write is in progress
        if (doWrite)
        {
          // Do a write
          attWriteReq_t req;
    
          // Allocate GATT write request
          req.pValue = GATT_bm_alloc(connHandleMap[index].connHandle, ATT_WRITE_REQ, 1, NULL);
          // If successfully allocated
          if (req.pValue != NULL)
          {
            // Fill up request
            req.handle = discInfo[index].charHdl;
            req.len = 1;
            req.pValue[0] = charVal;
            req.sig = 0;
            req.cmd = 0;
    
            // Send GATT write to controller
            status = GATT_WriteCharValue(connHandleMap[index].connHandle, &req, selfEntity);
    
            // If not sucessfully sent
            if ( status != SUCCESS )
            {
              // Free write request as the controller will not
              GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
            }
          }
        }
        // Do a read
        else
        {
          // Create read request...place in CSTACK
          attReadReq_t req;
    
          // Fill up read request
          req.handle = discInfo[index].charHdl;
    
          // Send read request. no need to free if unsuccessful since the request
          // is only placed in CSTACK; not allocated
          status = GATT_ReadCharValue(connHandleMap[index].connHandle, &req, selfEntity);
        }
    
        // If succesfully queued in controller
        if (status == SUCCESS)
        {
          // Toggle read / write
          doWrite = !doWrite;
        }
      }
    
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      mr_doConnUpdate
    *
    * @brief   Respond to user input to do a connection update
    *
    * @param   index - index as selected from the mrMenuConnUpdate
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doConnUpdate(uint8_t index)
    {
      bStatus_t status = FAILURE;
      // Fill in connection handle in dummy params
      updateParams.connHandle = connHandleMap[index].connHandle;
    
      // Send connection parameter update
      status = gapRole_connUpdate( GAPROLE_NO_ACTION, &updateParams);
    
      // If successfully sent to controller
      if (status == SUCCESS)
      {
    
      }
      // If there is already an ongoing update
      else if (status == blePending)
      {
    
      }
    
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      mr_doDisconnect
    *
    * @brief   Respond to user input to terminate a connection
    *
    * @param   index - index as selected from the mrMenuConnUpdate
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doDisconnect(uint8_t index)
    {
      // Disconnect
      GAPRole_TerminateConnection(connHandleMap[index].connHandle);
    
      return TRUE;
    }
    
    /* Actions for Menu: Init - Advertise */
    bool mr_doAdvertise(uint8_t index)
    {
      (void) index;
      uint8_t adv;
      uint8_t adv_status;
    
      // Get current advertising status
      GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &adv_status, NULL);
    
      // If we're currently advertising
      if (adv_status)
      {
        // Turn off advertising
        adv = FALSE;
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &adv, NULL);
      }
      // If we're not currently advertising
      else
      {
        // Turn on advertising
        adv = TRUE;
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &adv, NULL);
      }
    
      return TRUE;
    }
    
    /*********************************************************************
    *********************************************************************/
    

  • Hi Sean,

    Have you reproduced the problem? Can you suggest me which SdK mean while I should use. Non of our multi role is working. Stack 5 and stack 4.2 both gets crashed.

    I have to deliver multirole at start of next week. Our production team have to perform tests for longer time and with more connections which is currently not possible as the application has bug.
  • Hello,

    We are looking into the issue internally. This will take some time.

    For right now, the best advice I can provide is to suppress the hardware assert for the time being until we have found a root cause.

  • Hi Sean,

    How I can suppress? Just by de registering the assert handler? Then the hardware will work normally for long period of time ?

    I am waiting for your kind reply.
  • Hello,

    We are still looking into this. You can suppress by removing the call to hal_assert at the time of the issue.

    The likely case is that the invalid command will be rejected by the radio and you may miss an event.

  • I have reproduced the issue and reported to the R&D team.
    Here are the results of the investigation:
    - When the issue occurs it is not fatal to the stack, the stack will continue both scanning, advertising, and remain in the connection as a slave.
    - Commenting out the call to AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0); allows the application to continue as expected
    - The error codes originates from the BLE Link Layer receiving an unexpected (Pending) command from the RF driver.
    - When the issue occurs no advertising, scanning or connected packets are lost.

    A bit of feedback:
    - You are calling mr_doScan() based on a clock event that is triggered every 300ms (SCANNING_EVT_PERIOD)
    - The scan parameters have an interval and window of 50ms with a duration of 4s
    - Note : subsequent calls to GAPRole_StartDiscovery() once a scan has already started will return bleAlreadyInRequestedMode, it would make sense to align your SCANNING_EVT_PERIOD and your DEFAULT_SCAN_DURATION (i.e. SCANNING_EVT_PERIOD >= DEFAULT_SCAN_DURATION ) otherwise you will continually be returning errors from the GAP layer
    - Furthermore, mr_doScan() will call GAPRole_CancelDiscovery() if scanningStarted = TRUE (which would have been set at your last clock timeout), so your effective scan interval is ~ 2*SCANNING_EVT_PERIOD (on the first expiry you will cancel an existing scan, on the second you will start)
    - This method puts a lot of stress on the controller and GAP because it is starting and stopping scanning very often

    Instead, I would recommend to make SCANNING_EVT_PERIOD > DEFAULT_SCAN_DURATION, also since mr_doScan is now called via a clock object and not a menu I would recove the call to GAPRole_CancelDiscovery() since I don't believe that you intend to cancel scanning.