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.

CC254x Central with multiple peripherals

Other Parts Discussed in Thread: CC2640, CC2540, CC2541, CC2541EMK

Hi,

In the forum this question has been asked multiple times, but not answered.

I found two relevant projects: SimpleBLESwitch; and modified to serve two peripherals SimpleBLECentral. Niether one provides a clear understanding on how to scan, connect, and control multiple peripherals concurrently.

Is there a manual covering this topic available, or maybe a sample project?

Thanks.

  • I don't think the simpleBLESwitch is needed here. This is meant to switch between central and peripheral roles.

    It sounds like what you are looking for is a simpleBLECentral modified to work with multiple peripherals. There is an example of this for the 1.3 installer: processors.wiki.ti.com/.../SimpleBLECentral_Connect_to_Multi_SimpleBLEPeripheral_devices

    It has not been ported to newer versions of the stack but this should provide a good starting point.
  • Hi Tim,
    the sample code of the central device controlling multiple peripherals is practically a joke. It is nothing. I have to research every single step from scanning to connecting to data exchange. it would be much simpler if TI would have provided a basic code showing a concurrent multiple peripherals operation.

    Question: Writing a char to 2 peripherals at the same time:
    GATT_WriteCharValue(handle1);
    GATT_WriteCharValue(handle2);
    Call often returns status blePending when called more than once times. What would be a correct method of doing it? In general how multiple peripheral should be managed in the app using the BLE 1.4 stack?
    Thanks,
  • Hi
    As I know, you cannot control two or more devices at the same time. You need control them one by one. You can refer to "CC2640 Central to MultiPeripheral", this sample code implement that how the central write/read data to device.
    processors.wiki.ti.com/.../CC2640_Central_to_MultiPeripheral
  • According to Release Notes for stack Version 1.2:
    "- Multi-role and combo-role support has been enhanced. The BLE stack can now
    support simultaneously advertising and/or scanning while in a connection as
    either a master or a slave. This allows for a central device to perform
    device discovery while in a connection..."
    I can confirm that I am able to control 3 devices with v.1.4.2.
    The problem is the device discovery. It stops soon after a first peripheral is connected even calls to GAPCentralRole_StartDiscovery() continue returning SUCCESS. Discovery restarts working again as soon as last connection is terminated.
    I have a vary basic source code managing multiple connections. Is there a way to submit it for review?
    Thanks.
  • Hello. You should be able to post it on the wiki page: processors.wiki.ti.com/.../CC254X_Embedded_Examples
  • I don't have permission to edit wiki. Is there any other way to upload just one file simpleBLECentral.c for review?
  • Hello George,

    You can use the 'rich formatting option' to attach a file to E2E. We can review once you post your changes.

    Best wishes

  • simpleBLECentral.c - modified to serve Multiple connections.

    File:

    // Modified to support multiple peripherals
    // Automatically scanning for new devices; performs connection and service discovery
    // Send simple message to all connected devices

    // Buttons and LCD handling is partialy removed

    // Known issues:
    // 1. Device discovery stops soon after first device is connected. Discovery resumes when all connections are terminated.
    // 2. After connection and service discovery first attemp to write to characteristic fails.

    /******************************************************************************

    @file simpleBLECentral.c

    @brief This file contains the Simple BLE Central sample application for use
    with the CC2540 Bluetooth Low Energy Protocol Stack.

    Group: WCS, BTS
    Target Device: CC2540, CC2541

    ******************************************************************************

    Copyright (c) 2010-2016, Texas Instruments Incorporated
    All rights reserved.

    IMPORTANT: Your use of this Software is limited to those specific rights
    granted under the terms of a software license agreement between the user
    who downloaded the software, his/her employer (which must be your employer)
    and Texas Instruments Incorporated (the "License"). You may not use this
    Software unless you agree to abide by the terms of the License. The License
    limits your use, and you acknowledge, that the Software may not be modified,
    copied or distributed unless embedded on a Texas Instruments microcontroller
    or used solely and exclusively in conjunction with a Texas Instruments radio
    frequency transceiver, which is integrated into your product. Other than for
    the foregoing purpose, you may not use, reproduce, copy, prepare derivative
    works of, modify, distribute, perform, display or sell this Software and/or
    its documentation for any purpose.

    YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
    PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
    INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
    NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
    TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
    NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
    LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
    INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
    OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
    OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
    (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

    Should you have any questions regarding your right to use this Software,
    contact Texas Instruments Incorporated at www.TI.com.

    ******************************************************************************
    Release Name: ble_sdk_1.4.2.2
    Release Date: 2016-06-09 06:57:10
    *****************************************************************************/

    /*********************************************************************
    * INCLUDES
    */

    #include "bcomdef.h"
    #include "OSAL.h"
    #include "OSAL_PwrMgr.h"
    #include "OnBoard.h"
    #include "hal_led.h"
    #include "hal_key.h"
    #include "hal_lcd.h"
    #include "gatt.h"
    #include "ll.h"
    #include "hci.h"
    #include "gapgattserver.h"
    #include "gattservapp.h"
    #include "central.h"
    #include "gapbondmgr.h"
    #include "simpleGATTprofile.h"
    #include "simpleBLECentral.h"

    /*********************************************************************
    * MACROS
    */

    // Length of bd addr as a string
    #define B_ADDR_STR_LEN 15

    /*********************************************************************
    * CONSTANTS
    */

    // Maximum number of scan responses
    #define DEFAULT_MAX_SCAN_RES 8

    // Scan duration in ms
    #define DEFAULT_SCAN_DURATION 1000

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

    // Default RSSI polling period in ms
    #define DEFAULT_RSSI_PERIOD 1000

    // Whether to enable automatic parameter update request when a connection is formed
    #define DEFAULT_ENABLE_UPDATE_REQUEST FALSE

    // Minimum connection interval (units of 1.25ms) if automatic parameter update request is enabled
    #define DEFAULT_UPDATE_MIN_CONN_INTERVAL 400

    // Maximum connection interval (units of 1.25ms) if automatic parameter update request is enabled
    #define DEFAULT_UPDATE_MAX_CONN_INTERVAL 800

    // Slave latency to use if automatic parameter update request is enabled
    #define DEFAULT_UPDATE_SLAVE_LATENCY 0

    // Supervision timeout value (units of 10ms) if automatic parameter update request is enabled
    #define DEFAULT_UPDATE_CONN_TIMEOUT 600

    // Default passcode
    #define DEFAULT_PASSCODE 19655

    // Default GAP pairing mode
    #define DEFAULT_PAIRING_MODE GAPBOND_PAIRING_MODE_WAIT_FOR_REQ

    // Default MITM mode (TRUE to require passcode or OOB when pairing)
    #define DEFAULT_MITM_MODE FALSE

    // Default bonding mode, TRUE to bond
    #define DEFAULT_BONDING_MODE TRUE

    // Default GAP bonding I/O capabilities
    #define DEFAULT_IO_CAPABILITIES GAPBOND_IO_CAP_DISPLAY_ONLY

    // Default service discovery timer delay in ms
    #define DEFAULT_SVC_DISCOVERY_DELAY 100

    // TRUE to filter discovery results on desired service UUID
    #define DEFAULT_DEV_DISC_BY_SVC_UUID TRUE

    #define PERIODIC_EVT 0x0004
    #define PERIODIC_EVT_PERIOD 100 // msec

    CONST uint8 zero_address[B_ADDR_LEN] = {0,0,0,0,0,0};

    /*********************************************************************
    * TYPEDEFS
    */
    // Discovery states
    enum
    {
    BLE_DISC_STATE_IDLE, // Idle
    BLE_DISC_STATE_SVC, // Service discovery
    BLE_DISC_STATE_CHAR // Characteristic discovery
    };

    typedef enum
    {
    NONE = 0,
    ADVERTISING,
    VALIDATED,
    CONNECTED,
    }clientMode_t;

    typedef struct
    {
    clientMode_t mode;
    uint16 connHandle;
    uint16 charHdl; // Discovered characteristic handle
    gapDevRec_t record;
    } deviceList_t;

    /*********************************************************************
    * GLOBAL VARIABLES
    */

    /*********************************************************************
    * EXTERNAL VARIABLES
    */

    /*********************************************************************
    * EXTERNAL FUNCTIONS
    */

    /*********************************************************************
    * LOCAL VARIABLES
    */

    // Task ID for internal task/event processing
    static uint8 simpleBLETaskId;

    // GAP GATT Attributes
    static const uint8 simpleBLEDeviceName[GAP_DEVICE_NAME_LEN] = "Simple BLE Central";

    // Max number of connected peripheral devices
    #define MAX_CONNECTIONS 3

    // Device list with char and conn.handles
    static deviceList_t deviceList[MAX_CONNECTIONS];
    // Index of the device we trying to connect to
    static uint8 connectIdx;

    // Scanning state
    static uint8 simpleBLEScanning = FALSE;

    // RSSI polling state
    //static uint8 simpleBLERssi = FALSE;

    // Discovery state
    static uint8 simpleBLEDiscState = BLE_DISC_STATE_IDLE;

    // Discovered service start and end handle
    static uint16 simpleBLESvcStartHdl = 0;
    static uint16 simpleBLESvcEndHdl = 0;

    // Value to write
    static uint8 simpleBLECharVal = 0;

    // GATT read/write procedure state
    static bool rwProcedureInProgress[MAX_CONNECTIONS] = {FALSE, FALSE, FALSE};

    // Number of currently connected devices
    static uint8 NumberOfDevices;

    // TX buffer
    uint8 message[20];

    /*********************************************************************
    * LOCAL FUNCTIONS
    */
    static void simpleBLECentralProcessGATTMsg( gattMsgEvent_t *pMsg );
    static void simpleBLECentralRssiCB( uint16 connHandle, int8 rssi );
    static uint8 simpleBLECentralEventCB( gapCentralRoleEvent_t *pEvent );
    static void simpleBLECentralPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
    uint8 uiInputs, uint8 uiOutputs );
    static void simpleBLECentralPairStateCB( uint16 connHandle, uint8 state, uint8 status );
    //static void simpleBLECentral_HandleKeys( uint8 shift, uint8 keys );
    static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg );
    static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg, uint8 idx );
    static void simpleBLECentralStartDiscovery( void );
    static bool simpleBLEFindSvcUuid( uint16 uuid, uint8 *pData, uint8 dataLen );
    char *bdAddr2Str ( uint8 *pAddr );
    static void simpleBLE_Process(void);
    static void AddDeviceInfo( uint8 *, uint8);
    static uint8 FindDeviceByAddress( uint8 *);
    static uint8 FindDeviceByHandle(uint16);
    static void RemoveDeviceByHandle(uint16);
    static bool EmptySlotAvailable(void);
    static uint8 isNewDevToConnect(void);

    /*********************************************************************
    * PROFILE CALLBACKS
    */

    // GAP Role Callbacks
    static const gapCentralRoleCB_t simpleBLERoleCB =
    {
    simpleBLECentralRssiCB, // RSSI callback
    simpleBLECentralEventCB // Event callback
    };

    // Bond Manager Callbacks
    static const gapBondCBs_t simpleBLEBondCB =
    {
    simpleBLECentralPasscodeCB,
    simpleBLECentralPairStateCB
    };

    /*********************************************************************
    * PUBLIC FUNCTIONS
    */

    /*********************************************************************
    * @fn SimpleBLECentral_Init
    *
    * @brief Initialization function for the Simple BLE Central App Task.
    * This is called during initialization and should contain
    * any application specific initialization (ie. hardware
    * initialization/setup, table initialization, power up
    * notification).
    *
    * @param task_id - the ID assigned by OSAL. This ID should be
    * used to send messages and set timers.
    *
    * @return none
    */
    void SimpleBLECentral_Init( uint8 task_id )
    {
    simpleBLETaskId = task_id;

    // Setup Central Profile
    {
    uint8 scanRes = DEFAULT_MAX_SCAN_RES;
    GAPCentralRole_SetParameter ( GAPCENTRALROLE_MAX_SCAN_RES, sizeof( uint8 ), &scanRes );
    }

    // Setup GAP
    GAP_SetParamValue( TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION );
    GAP_SetParamValue( TGAP_LIM_DISC_SCAN, DEFAULT_SCAN_DURATION );
    GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, (uint8 *) simpleBLEDeviceName );

    // Setup the GAP Bond Manager
    {
    uint32 passkey = DEFAULT_PASSCODE;
    uint8 pairMode = DEFAULT_PAIRING_MODE;
    uint8 mitm = DEFAULT_MITM_MODE;
    uint8 ioCap = DEFAULT_IO_CAPABILITIES;
    uint8 bonding = DEFAULT_BONDING_MODE;
    GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof( uint32 ), &passkey );
    GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof( uint8 ), &pairMode );
    GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof( uint8 ), &mitm );
    GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof( uint8 ), &ioCap );
    GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof( uint8 ), &bonding );
    }

    // Initialize GATT Client
    VOID GATT_InitClient();

    // Register to receive incoming ATT Indications/Notifications
    GATT_RegisterForInd( simpleBLETaskId );

    // Initialize GATT attributes
    GGS_AddService( GATT_ALL_SERVICES ); // GAP
    GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes

    // Register for all key events - This app will handle all key events
    RegisterForKeys( simpleBLETaskId );

    // makes sure LEDs are off
    HalLedSet( (HAL_LED_1 | HAL_LED_2), HAL_LED_MODE_OFF );

    // Setup a delayed profile startup
    osal_set_event( simpleBLETaskId, START_DEVICE_EVT );

    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    deviceList[i].connHandle = GAP_CONNHANDLE_INIT;
    }
    }

    /*********************************************************************
    * @fn SimpleBLECentral_ProcessEvent
    *
    * @brief Simple BLE Central Application Task event processor. This function
    * is called to process all events for the task. Events
    * include timers, messages and any other user defined events.
    *
    * @param task_id - The OSAL assigned task ID.
    * @param events - events to process. This is a bit map and can
    * contain more than one event.
    *
    * @return events not processed
    */
    uint16 SimpleBLECentral_ProcessEvent( uint8 task_id, uint16 events )
    {

    VOID task_id; // OSAL required parameter that isn't used in this function

    if ( events & SYS_EVENT_MSG )
    {
    uint8 *pMsg;

    if ( (pMsg = osal_msg_receive( simpleBLETaskId )) != NULL )
    {
    simpleBLECentral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

    // Release the OSAL message
    VOID osal_msg_deallocate( pMsg );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
    }

    if ( events & START_DEVICE_EVT )
    {
    // Start the Device
    VOID GAPCentralRole_StartDevice( (gapCentralRoleCB_t *) &simpleBLERoleCB );

    // Register with bond manager after starting device
    GAPBondMgr_Register( (gapBondCBs_t *) &simpleBLEBondCB );

    // Set timer for first periodic event
    osal_start_timerEx( simpleBLETaskId, PERIODIC_EVT, PERIODIC_EVT_PERIOD );

    return ( events ^ START_DEVICE_EVT );
    }

    if ( events & PERIODIC_EVT )
    {
    // Restart timer
    osal_start_timerEx( simpleBLETaskId, PERIODIC_EVT, PERIODIC_EVT_PERIOD );

    simpleBLE_Process();

    return (events ^ PERIODIC_EVT);
    }

    if ( events & START_DISCOVERY_EVT )
    {
    simpleBLECentralStartDiscovery( );

    return ( events ^ START_DISCOVERY_EVT );
    }

    // Discard unknown events
    return 0;
    }

    /*********************************************************************
    * @fn simpleBLECentral_ProcessOSALMsg
    *
    * @brief Process an incoming task message.
    *
    * @param pMsg - message to process
    *
    * @return none
    */
    static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
    {
    switch ( pMsg->event )
    {
    case KEY_CHANGE:
    break;

    case GATT_MSG_EVENT:
    simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );
    break;
    }
    }

    static void simpleBLE_Process()
    {
    uint8 idx;

    // ---------------- SCAN and CONNECT to new devices ------------------
    if (!simpleBLEScanning && ( simpleBLEDiscState == BLE_DISC_STATE_IDLE ))
    {
    // attempt connection to newly found device
    if ((idx = isNewDevToConnect()) != 0xFF)
    {
    uint8 addrType;
    uint8 *peerAddr;
    // connect to device
    peerAddr = deviceList[idx].record.addr;
    addrType = deviceList[idx].record.addrType;
    GAPCentralRole_EstablishLink (DEFAULT_LINK_HIGH_DUTY_CYCLE,
    DEFAULT_LINK_WHITE_LIST,
    addrType, peerAddr );
    simpleBLEDiscState = BLE_DISC_STATE_SVC; // block until service discovery is done
    }else if ((EmptySlotAvailable()))
    {
    // scan if we have a room for new connections
    GAPCentralRole_StartDiscovery( DEVDISC_MODE_ALL,
    DEFAULT_DISCOVERY_ACTIVE_SCAN,
    DEFAULT_LINK_WHITE_LIST );
    simpleBLEScanning = TRUE;
    }
    }

    // ---------------- Check if there is a device to control ------------------
    NumberOfDevices = 0;
    for (int i=0; i < MAX_CONNECTIONS; i++)
    {
    if (deviceList[i].charHdl != 0)
    {
    NumberOfDevices++;
    }
    }
    if (NumberOfDevices == 0)
    {
    return; // no device to control
    }

    // ---------------- Check if last command has been delivered to every device ------------------
    for (int i=0; i < MAX_CONNECTIONS; i++)
    {
    if ( rwProcedureInProgress[i] == TRUE )
    return;
    }
    // ---------------- CONTROL connected devices ------------------
    // preparing and sending message
    for (int i=0; i < MAX_CONNECTIONS; i++)
    {
    if (deviceList[i].charHdl)
    {
    uint8 status;
    attWriteReq_t req;
    req.pValue = GATT_bm_alloc( deviceList[i].connHandle, ATT_WRITE_REQ, sizeof(message), NULL );

    if ( req.pValue != NULL )
    {
    message[0]= simpleBLECharVal;
    req.handle = deviceList[i].charHdl + 1;
    req.len = sizeof(message);
    osal_memcpy( req.pValue, (uint8*)&message, sizeof(message));
    req.sig = 0;
    req.cmd = 0;
    status = GATT_WriteCharValue( deviceList[i].connHandle, &req, simpleBLETaskId );
    if ( status != SUCCESS )
    {
    GATT_bm_free( (gattMsg_t *)&req, ATT_WRITE_REQ );
    }else{
    rwProcedureInProgress[i] = TRUE;
    }
    }
    }
    }
    }

    /*********************************************************************
    * @fn simpleBLECentralProcessGATTMsg
    *
    * @brief Process GATT messages
    *
    * @return none
    */
    static void simpleBLECentralProcessGATTMsg( gattMsgEvent_t *pMsg )
    {
    uint8 idx;
    if ((pMsg->connHandle == GAP_CONNHANDLE_INIT) || // just in case if handle comes already reset
    ((idx = FindDeviceByHandle( pMsg->connHandle )) == 0xFF))
    {
    // In case a GATT message came after a connection has dropped,
    // ignore the message
    return;
    }

    if ( ( pMsg->method == ATT_READ_RSP ) ||
    ( ( pMsg->method == ATT_ERROR_RSP ) &&
    ( pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ ) ) )
    {
    rwProcedureInProgress[idx] = FALSE;
    }
    else if ( ( pMsg->method == ATT_WRITE_RSP ) ||
    ( ( pMsg->method == ATT_ERROR_RSP ) &&
    ( pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ ) ) )
    {
    rwProcedureInProgress[idx] = FALSE;
    }
    else if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE )
    {
    simpleBLEGATTDiscoveryEvent( pMsg, idx );
    }
    GATT_bm_free( &pMsg->msg, pMsg->method );
    }

    /*********************************************************************
    * @fn simpleBLECentralRssiCB
    *
    * @brief RSSI callback.
    *
    * @param connHandle - connection handle
    * @param rssi - RSSI
    *
    * @return none
    */
    static void simpleBLECentralRssiCB( uint16 connHandle, int8 rssi )
    {
    LCD_WRITE_STRING_VALUE( "RSSI -dB:", (uint8) (-rssi), 10, HAL_LCD_LINE_1 );
    }

    /*********************************************************************
    * @fn simpleBLECentralEventCB
    *
    * @brief Central event callback function.
    *
    * @param pEvent - pointer to event structure
    *
    * @return TRUE if safe to deallocate event message, FALSE otherwise.
    */
    static uint8 simpleBLECentralEventCB( gapCentralRoleEvent_t *pEvent )
    {
    uint8 idx;

    switch ( pEvent->gap.opcode )
    {
    case GAP_DEVICE_INIT_DONE_EVENT:
    {
    LCD_WRITE_STRING( "BLE Central", HAL_LCD_LINE_1 );
    LCD_WRITE_STRING( bdAddr2Str( pEvent->initDone.devAddr ), HAL_LCD_LINE_2 );
    }
    break;

    case GAP_DEVICE_INFO_EVENT:
    {
    // if filtering device discovery results based on service UUID
    if ( DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE )
    {
    if ( simpleBLEFindSvcUuid( SIMPLEPROFILE_SERV_UUID,
    pEvent->deviceInfo.pEvtData,
    pEvent->deviceInfo.dataLen ) )
    { // advertisement. Event #1
    AddDeviceInfo( pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType );
    }else if ((pEvent->deviceInfo.eventType == 4))
    { // scan response. Event #2
    if ((idx = FindDeviceByAddress(pEvent->deviceInfo.addr)) != 0xFF)
    {
    deviceList[idx].mode = VALIDATED;
    }
    if ( !EmptySlotAvailable()) // if the list is already full
    {
    GAPCentralRole_CancelDiscovery();
    }
    }
    }
    }
    break;

    case GAP_DEVICE_DISCOVERY_EVENT:
    {
    // discovery complete
    simpleBLEScanning = FALSE;
    }
    break;

    case GAP_LINK_ESTABLISHED_EVENT:
    {
    if ((idx = FindDeviceByAddress(pEvent->linkCmpl.devAddr)) != 0xFF)
    {
    if ( pEvent->gap.hdr.status == SUCCESS )
    {
    deviceList[idx].connHandle = pEvent->linkCmpl.connectionHandle;
    deviceList[idx].mode = CONNECTED;
    connectIdx = idx;
    osal_start_timerEx( simpleBLETaskId, START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );
    return TRUE;
    }else{
    osal_memset(&deviceList[idx], 0, sizeof(deviceList_t));
    deviceList[idx].connHandle = GAP_CONNHANDLE_INIT;
    }
    }
    simpleBLEDiscState = BLE_DISC_STATE_IDLE; // Done with service discovery if we have reached here
    }
    break;

    case GAP_LINK_TERMINATED_EVENT:
    {
    RemoveDeviceByHandle(pEvent->linkTerminate.connectionHandle);
    }
    break;

    case GAP_LINK_PARAM_UPDATE_EVENT:
    {
    LCD_WRITE_STRING( "Param Update", HAL_LCD_LINE_1 );
    }
    break;

    default:
    break;
    }
    return ( TRUE );
    }

    /*********************************************************************
    * @fn pairStateCB
    *
    * @brief Pairing state callback.
    *
    * @return none
    */
    static void simpleBLECentralPairStateCB( uint16 connHandle, uint8 state, uint8 status )
    {
    if ( state == GAPBOND_PAIRING_STATE_STARTED )
    {
    LCD_WRITE_STRING( "Pairing started", HAL_LCD_LINE_1 );
    }
    else if ( state == GAPBOND_PAIRING_STATE_COMPLETE )
    {
    if ( status == SUCCESS )
    {
    LCD_WRITE_STRING( "Pairing success", HAL_LCD_LINE_1 );
    }
    else
    {
    LCD_WRITE_STRING_VALUE( "Pairing fail", status, 10, HAL_LCD_LINE_1 );
    }
    }
    else if ( state == GAPBOND_PAIRING_STATE_BONDED )
    {
    if ( status == SUCCESS )
    {
    LCD_WRITE_STRING( "Bonding success", HAL_LCD_LINE_1 );
    }
    }
    }

    /*********************************************************************
    * @fn simpleBLECentralPasscodeCB
    *
    * @brief Passcode callback.
    *
    * @return none
    */
    static void simpleBLECentralPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
    uint8 uiInputs, uint8 uiOutputs )
    {
    #if (HAL_LCD == TRUE)

    uint32 passcode;
    uint8 str[7];

    // Create random passcode
    LL_Rand( ((uint8 *) &passcode), sizeof( uint32 ));
    passcode %= 1000000;

    // Display passcode to user
    if ( uiOutputs != 0 )
    {
    LCD_WRITE_STRING( "Passcode:", HAL_LCD_LINE_1 );
    LCD_WRITE_STRING( (char *) _ltoa(passcode, str, 10), HAL_LCD_LINE_2 );
    }

    // Send passcode response
    GAPBondMgr_PasscodeRsp( connectionHandle, SUCCESS, passcode );
    #endif
    }

    /*********************************************************************
    * @fn simpleBLECentralStartDiscovery
    *
    * @brief Start service discovery.
    *
    * @return none
    */
    static void simpleBLECentralStartDiscovery( void )
    {
    uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
    HI_UINT16(SIMPLEPROFILE_SERV_UUID) };

    // Initialize cached handles
    simpleBLESvcStartHdl = simpleBLESvcEndHdl = 0;

    // simpleBLEDiscState = BLE_DISC_STATE_SVC;

    // Discovery simple BLE service
    GATT_DiscPrimaryServiceByUUID( deviceList[connectIdx].connHandle,
    uuid,
    ATT_BT_UUID_SIZE,
    simpleBLETaskId );
    }

    /*********************************************************************
    * @fn simpleBLEGATTDiscoveryEvent
    *
    * @brief Process GATT discovery event
    *
    * @return none
    */
    static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg, uint8 idx )
    {
    attReadByTypeReq_t req;

    if ( simpleBLEDiscState == BLE_DISC_STATE_SVC )
    {
    // Service found, store handles
    if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
    pMsg->msg.findByTypeValueRsp.numInfo > 0 )
    {
    simpleBLESvcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
    simpleBLESvcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
    }

    // If procedure complete
    if ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
    pMsg->hdr.status == bleProcedureComplete ) ||
    ( pMsg->method == ATT_ERROR_RSP ) )
    {
    if ( simpleBLESvcStartHdl != 0 )
    {
    // Discover characteristic
    simpleBLEDiscState = BLE_DISC_STATE_CHAR;

    req.startHandle = simpleBLESvcStartHdl;
    req.endHandle = simpleBLESvcEndHdl;
    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);

    GATT_ReadUsingCharUUID( deviceList[idx].connHandle, &req, simpleBLETaskId );
    }
    }
    }
    else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR )
    {
    // Characteristic found, store handle
    if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&
    pMsg->msg.readByTypeRsp.numPairs > 0 )
    {
    deviceList[idx].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
    pMsg->msg.readByTypeRsp.pDataList[1]);

    LCD_WRITE_STRING( "Simple Svc Found", HAL_LCD_LINE_1 );
    // simpleBLEProcedureInProgress = FALSE;
    }
    // If procedure complete
    if ( ( pMsg->method == ATT_READ_BY_TYPE_RSP &&
    pMsg->hdr.status == bleProcedureComplete ) ||
    ( pMsg->method == ATT_ERROR_RSP ) )
    {
    simpleBLEDiscState = BLE_DISC_STATE_IDLE;
    }
    }
    }


    /*********************************************************************
    * @fn simpleBLEFindSvcUuid
    *
    * @brief Find a given UUID in an advertiser's service UUID list.
    *
    * @return TRUE if service UUID found
    */
    static bool simpleBLEFindSvcUuid( uint16 uuid, uint8 *pData, uint8 dataLen )
    {
    uint8 adLen;
    uint8 adType;
    uint8 *pEnd;

    pEnd = pData + dataLen - 1;

    // While end of data not reached
    while ( pData < pEnd )
    {
    // Get length of next AD item
    adLen = *pData++;
    if ( adLen > 0 )
    {
    adType = *pData;

    // If AD type is for 16-bit service UUID
    if ( adType == GAP_ADTYPE_16BIT_MORE || adType == GAP_ADTYPE_16BIT_COMPLETE )
    {
    pData++;
    adLen--;

    // For each UUID in list
    while ( adLen >= 2 && pData < pEnd )
    {
    // Check for match
    if ( pData[0] == LO_UINT16(uuid) && pData[1] == HI_UINT16(uuid) )
    {
    // Match found
    return TRUE;
    }

    // Go to next
    pData += 2;
    adLen -= 2;
    }

    // Handle possible erroneous extra byte in UUID list
    if ( adLen == 1 )
    {
    pData++;
    }
    }
    else
    {
    // Go to next item
    pData += adLen;
    }
    }
    }

    // Match not found
    return FALSE;
    }

    static void AddDeviceInfo( uint8 *pAddr, uint8 addrType )
    {
    uint8 idx = 0xFF;

    // Check if device is already in the scan results
    for (uint8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if ( osal_memcmp( pAddr, deviceList[i].record.addr , B_ADDR_LEN ) )
    {
    return; // already in the list
    }
    else if ((idx == 0xFF) && ( osal_memcmp( zero_address, deviceList[i].record.addr, B_ADDR_LEN )))
    {
    idx = i; // save the firt empty spot index
    }
    }

    if (idx != 0xFF)
    {
    osal_memcpy( deviceList[idx].record.addr, pAddr, B_ADDR_LEN );
    deviceList[idx].record.addrType = addrType;
    deviceList[idx].mode = ADVERTISING;
    }
    }

    static void RemoveDeviceByHandle(uint16 connHandle)
    {
    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if ( connHandle == deviceList[i].connHandle )
    {
    osal_memset(&deviceList[i], 0, sizeof(deviceList_t));
    deviceList[i].connHandle = GAP_CONNHANDLE_INIT;
    rwProcedureInProgress[i] = FALSE;
    return;
    }
    }
    }

    static bool EmptySlotAvailable(void)
    {
    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if ( osal_memcmp( zero_address, deviceList[i].record.addr , B_ADDR_LEN ))
    return TRUE;
    }
    return FALSE;
    }

    static uint8 isNewDevToConnect(void)
    {
    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if (deviceList[i].mode == VALIDATED)
    {
    return i;
    }
    }
    return 0xFF;
    }

    static uint8 FindDeviceByHandle(uint16 connHandle) // 0 is a valid handle!
    {
    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if ( connHandle == deviceList[i].connHandle )
    {
    return i; // return device index
    }
    }
    return 0xFF; // handle not found
    }

    static uint8 FindDeviceByAddress( uint8 *pAddr )
    {
    for (int8 i = 0; i < MAX_CONNECTIONS; i++ )
    {
    if ( osal_memcmp( pAddr, deviceList[i].record.addr, B_ADDR_LEN ) )
    {
    return i; // return device index
    }
    }
    return 0xFF; // handle not found
    }

    /*********************************************************************
    * @fn bdAddr2Str
    *
    * @brief Convert Bluetooth address to string
    *
    * @return none
    */
    char *bdAddr2Str( uint8 *pAddr )
    {
    uint8 i;
    char hex[] = "0123456789ABCDEF";
    static char str[B_ADDR_STR_LEN];
    char *pStr = str;

    *pStr++ = '0';
    *pStr++ = 'x';

    // Start from end of addr
    pAddr += B_ADDR_LEN;

    for ( i = B_ADDR_LEN; i > 0; i-- )
    {
    *pStr++ = hex[*--pAddr >> 4];
    *pStr++ = hex[*pAddr & 0x0F];
    }

    *pStr = 0;

    return str;
    }

  • Hello,

    Thank you for the code. We will review and get back to you. For future reference, you can attach as a file using the rich formatting option.

    Best wishes
  • Hi,

    I did look through your code, it's not as simple as I expected, but I did not see anything wrong so far. But I would like to know if you receive the GAP_DEVICE_DISCOVERY_EVENT after sending the GAPCentralRole_StartDiscovery() command after making the first connection?

    Best wishes

  • Hi,
    I would like to make it as simple as possible. I just don't have enough information to accomplish it.

    To your question: Yes, I receive events GAP_DEVICE_DISCOVERY_EVENT for few more seconds after the first connection. After than no more such events until I kill the power on all connected devices and all connections are terminated.

    Thanks.
  • Ok. That means that the device has finished scanning successfully. Do you also get the GAP_DEVICE_INFO_EVENT for each discovered device?

    Best wishes
  • Yes, I get the GAP_DEVICE_INFO_EVENT for each discovered device during first several discovery cycles. The problem: info events stop soon after first connection is made. I keep calling GAPCentralRole_StartDiscovery(), BLE stack returns SUCCESS, but nothing happens. Until all previously made connections are terminated. Then GAP_DEVICE_INFO_EVENT resumes. To be clear, by connection terminated, I mean terminated by a timeout when peripheral devices were turned off.
    Thanks,
  • Hi George,

    I've added the central to multiple peripherals project with steps here:
    processors.wiki.ti.com/.../Central_to_MultiPeripherals_V1.4.2

    Please see if you are able to run the above project.

    Best wishes
  • Hi,
    TI provided many reference projects for the BLE peripheral, but only one highly simplified project for the central role.
    Can you, please, create another sample project for the central role - sensor network data collection: central device constantly scanning area for BLE peripherals; connecting to them (up to 3 device) and do some reading/writing. It would be great if central device will demonstrate the way to repair connections and connection using the bonded information saved in the non-volatile memory.

    I will be able to based our project on that sample.

    Right now I have my own code running, but scanning isn't working all the time. As I stated in previous posts: 1.4.2 stack always respond SUCCESS on GAPCentralRole_StartDiscovery() calls, I am receiving the discovery events GAP_DEVICE_DISCOVERY_EVENT when the discovery is finished, but events GAP_DEVICE_INFO_EVENT vanish soon after first connection is made. Sometimes GAP_DEVICE_INFO_EVENT events may work OK for minutes, sometime they come back after a while. I am not able to trace the bug since the stack source code isn't available.

    Please, help!!!

    Thanks,
  • Hi George,

    Were you able to confirm if the updated sample application for BLE 1.4.2 addressed your issue of not being able to scan while connected? Let's first confirm this is working your side, then add functions 1x1 to meet your application requirements.

    Some of the symptoms you describe could be due to memory heap exhaustion. Can you enable OSALMEM_METRICS to confirm if you are seeing memory allocation errors?

    Finally, I'm not sure what "demonstrate the way to repair connections and connection using the bonded information saved in the non-volatile memory" means. You can see an example of using the whitelist to establish a connection in the HIDAdvRemoteDongle sample application. See use of hidappEstablishLink() when GAPBOND_AUTO_SYNC_WL is TRUE.

    Best wishes
  • Hi JXS,
    We bought kits: CC2540DK and CC2541EMK to try the sample, but not able make it work -blank LCD.
    As you asked I enabled OSALMEM_METRICS in our project. Didn't notice any difference. Not sure what I should be looking for to catch the memory allocation errors.
    I set a breakpoing(log) in the "case GAP_DEVICE_INFO_EVENT:". Some time events keep happening for minutes after the first connection, some times just a few seconds. Events stops soon after two peripherals get connected.
    Thanks.
  • Hi George,

    Did you find a solution for your issue?

    I have the exact same issue actually and it is very annoying.

    Thank you.

    Best regards,
    Johann
  • Yes, as soon as on the central:

    1. I had disabled a change of connection parameters GAP_SetParamValue( TGAP_REJECT_CONN_PARAMS, FALSE)

    2. Set a connection interval to 80 (100 ms)

    After that supporting 3 connections became a breeze.

  • Hi George,

    1. Does it mean that you have added "GAP_SetParamValue(TGAP_REJECT_CONN_PARAMS, TRUE)" in the init function of your central?
    2. Did you adjust the connection interval in your peripherals?

    Thank you.

    Best regards,
    Johann

  • Hi Johann,

    1. Yes.

    2. This is not necessary after the step 1. Just set a proper interval on the Central device. 100 ms works the best for me. You may try shorter intervals as well.

    Regards,

    George.

  • Hi George,

    Thanks for your answers.

    How do you set the connection interval? Is there a stack API function for that?

    How much memory did you allocate to the heap? It seems that when I update my firmware with the function of point 1, the central resets automatically (maybe a heap overflow, I don't know).

    Regards,
    Johann
  • There are two calls in Central_Init():
    VOID GAP_SetParamValue( TGAP_CONN_EST_INT_MIN, min_connectionInterval );
    VOID GAP_SetParamValue( TGAP_CONN_EST_INT_MAX, max_connectionInterval );
    I set both intervals to 80 (100 ms).

    In the same Central_Init() put:
    VOID GAP_SetParamValue( TGAP_REJECT_CONN_PARAMS, TRUE);

    Do so, shouldn't reset the central.

    Regards,
  • Hi George,

    Thank you for your precious help.

    I did the modifications mentioned above and I am able to detect other peripheral while already connected to a peripheral.

    However, sometimes, the CC2541 resets automatically (jump to main HAL_BOARD_INIT() function) when a device is detected while connected to a peripheral. Did you face the same issue?

    Could you give me the following information:

    • Scan duration you use (DEFAULT_SCAN_DURATION),
    • Maximum number of scan responses you use (DEFAULT_MAX_SCAN_RES), and
    • The size of the heap you defined in the pre-processor options (INT_HEAP_LEN)?

    Thank you again.

    Regards,
    Johann

  • Hi Johann,

    glad I was able to help.

    The CPU reset problem isn't related to any of the settings you are asking about. The scan duration is just a matter of convenience: I was using 3 seconds, than switched to several attempts 500 ms long. Haven't touched the heap size, it is unmodified 3072 bytes.

    The bug is in your application. It took me a couple of months to figure out how stack operates. The TI sample central application is just a joke.