Part Number:CC2650MODA
I'm currently working on a Firmware for the CC2650MODA which enables me to connect from an Android Phone to the device and the device itself to another device (Phone <-> Device <-> Device).
I started with project_zero for a single connection. The phone can connect to the device, enable notifications for several characteristics and runs until the link is terminated by the user.
For multiple connections I used the multirole example. The firmware itself works as intended when using a CC2650-LaunchPad with the host_test and the "BLE Device Monitor". I can connect to the device, change characteristics as wanted and also make the device connect to another peripheral (LaunchPad <-> CC2650MODA<-> CC2650MODA). When using an android phone, the connection can be established, characteristics can be written to or read from, everything works fine. But after around 30 seconds, the device disconnects with reason 0x13 or 0x08.
I tried to setup the multirole peripheral as identical to project_zero as possible, but it did not help. Also tried disabling boding as discribed here, but it didnt work for me.
Project Zero:
TI v18.12.1LTS
XDCtools 3.32.0.06_core
TI-RTOS 2.20.1.08
BLE SDK 2_02_01_18
Multirole:
TI v18.12.1LTS
XDCtools 3.32.0.06_core
TI-RTOS 2.20.1.08
BLE SDK 2_02_01_18
Source Snippet project_zero:
void task_project_zero_init(void) { // ****************************************************************** // NO 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 via ICall to Stack. ICall_registerApp(&[projectName]_entity, &[projectName]_sem); // ****************************************************************** // BLE Stack initialization // ****************************************************************** // Setup the GAP Peripheral Role Profile uint8_t initialAdvertEnable = TRUE; // Advertise on power-up // By setting this to zero, the device will go into the waiting state after // being discoverable. Otherwise wait this long [ms] before advertising again. uint16_t advertOffTime = 0; // miliseconds // Set advertisement enabled. GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initialAdvertEnable); // Configure the wait-time before restarting advertisement automatically GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t), &advertOffTime); // Initialize Scan Response data GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); // Initialize Advertisement data GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); uint8_t enableUpdateRequest = DEFAULT_ENABLE_UPDATE_REQUEST; uint16_t desiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL; uint16_t desiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL; uint16_t desiredSlaveLatency = DEFAULT_DESIRED_SLAVE_LATENCY; uint16_t desiredConnTimeout = DEFAULT_DESIRED_CONN_TIMEOUT; GAPRole_SetParameter(GAPROLE_PARAM_UPDATE_ENABLE, sizeof(uint8_t), &enableUpdateRequest); GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desiredMinInterval); GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desiredMaxInterval); GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t), &desiredSlaveLatency); GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t), &desiredConnTimeout); // Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m", // (IArg)Util_getLocalNameStr(advertData)); // Set advertising interval 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); // Set duration of advertisement before stopping in Limited adv mode. GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds // ****************************************************************** // BLE Bond Manager initialization // ****************************************************************** uint32_t passkey = 0; // passkey "000000" uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; uint8_t mitm = TRUE; uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; uint8_t bonding = TRUE; GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode); GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm); GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding); // ****************************************************************** // BLE Service initialization // ****************************************************************** // Add services to GATT server GGS_AddService(GATT_ALL_SERVICES); // GAP GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes DevInfo_AddService(); // Device Information Service // Set the device name characteristic in the GAP Profile GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName); // User service // ... [Cut out] ... // Start the stack in Peripheral mode. VOID GAPRole_StartDevice(&[projectName]_gapRoleCBs); // Start Bond Manager VOID GAPBondMgr_Register(&[projectName]_bondMgrCBs); // Register with GAP for HCI/Host messages GAP_RegisterForMsgs([projectName]_entity); // Register for GATT local events and ATT Responses pending for transmission GATT_RegisterForMsgs([projectName]_entity); // User inits // ... [Cut out] ... }
Source Snippet Multirole
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, &sem); // Create an RTOS queue for message from profile to be sent to app. appMsgQueue = Util_constructQueue(&appMsg); //init keys and LCD Board_initKeys(multi_role_keyChangeHandler); // Open Display. dispHandle = Display_open(SBC_DISPLAY_TYPE, NULL); // Setup the GAP { /*-------------------PERIPHERAL-------------------*/ // 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); #warning modified // GAP_SetParamValue(TGAP_CONN_ADV_INT_MIN, advInt); // GAP_SetParamValue(TGAP_CONN_ADV_INT_MAX, advInt); // Set duration of advertisement before stopping in Limited adv mode. GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds /*-------------------CENTRAL-------------------*/ // 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; uint16_t desiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL; uint16_t desiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL; uint16_t desiredSlaveLatency = DEFAULT_DESIRED_SLAVE_LATENCY; uint16_t desiredConnTimeout = DEFAULT_DESIRED_CONN_TIMEOUT; // 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 connection parameters GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desiredMinInterval, NULL); GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desiredMaxInterval, NULL); GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t), &desiredSlaveLatency, NULL); GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t), &desiredConnTimeout, NULL); /*--------------CENTRAL-----------------*/ 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(uint16_t) * maxNumBleConns)) { // init index to connection handle map to 0's for (uint8_t i = 0; i < maxNumBleConns; i++) { connHandleMap[i] = 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; } } } // Setup the GAP Bond Manager { #warning modification uint32_t passkey = DEFAULT_PASSCODE; //uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE; //uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; uint8_t pairMode = GAPBOND_PAIRING_MODE_NO_PAIRING; uint8_t mitm = TRUE; uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; uint8_t bonding = TRUE; // set passcode GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); // set pairing mode GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode); // set authentication requirements GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm); // set i/o capabilities GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); // set bonding requirements GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding); // Register and start Bond Manager VOID GAPBondMgr_Register(&multi_role_BondMgrCBs); } //GATT { /*---------------------SERVER------------------------*/ // 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(); /************************************************************************** * Services *************************************************************************/ // Custom service here // ... [Cut out] ... // /*-----------------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); } // Additional user inits // ... [Cut out] ... }