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: if the BLE5 Simple Peripheral example has Coded PHY enabled by default?

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2592

Tool/software:

Hello Team,

I am using the CC2640R2F for a wireless sensor project. The ADC data is successfully transmitted to my phone (iPhone, iOS16, LightBlue app). However, I encountered an issue when trying to implement Long Range mode, where the transmission distance is only 15-20 meters, and beyond that, the connection disconnects. I tested the same setup with my colleague's Samsung Galaxy S21 (Android 14, LightBlue), but the issue persists, with the connection dropping. I am writing my code based on the BLE5 Simple Peripheral example from SDK 5_30_00_03 on CC2640R2F LaunchBoard.

In the SimplePeripheral_processGapMessage function, there is a section of the original code:

static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg)
{
switch(pMsg->opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
bStatus_t status = FAILURE;

gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;

if(pPkt->hdr.status == SUCCESS)
{
// Store the system ID
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];

// use 6 bytes of device address for 8 bytes of system ID value
systemId[0] = pPkt->devAddr[0];
systemId[1] = pPkt->devAddr[1];
systemId[2] = pPkt->devAddr[2];

// set middle bytes to zero
systemId[4] = 0x00;
systemId[3] = 0x00;

// shift three bytes up
systemId[7] = pPkt->devAddr[5];
systemId[6] = pPkt->devAddr[4];
systemId[5] = pPkt->devAddr[3];

// Set Device Info Service Parameter
DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);

Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized");

// Setup and start Advertising
// For more information, see the GAP section in the User's Guide:
// software-dl.ti.com/.../

// Temporary memory for advertising parameters for set #1. These will be copied
// by the GapAdv module
GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;

// Create Advertisement set #1 and assign handle
status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLegacy,
&advHandleLegacy);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

// Load advertising data for set #1 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

// Load scan response data for set #1 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
sizeof(scanRspData), scanRspData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

// Set event mask for set #1
status = GapAdv_setEventMask(advHandleLegacy,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);

// Enable legacy advertising for set #1
status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);


// Use long range params to create long range set #2
GapAdv_params_t advParamLongRange = GAPADV_PARAMS_AE_LONG_RANGE_CONN;

// Change the secPhy to S8. The primPhy has already been specified as S2 in the "GAPADV_PARAMS_AE_LONG_RANGE_CONN" preset, so here we are only modifying the secPhy, changing it from S2 to S8. However, whether we change the secPhy or not, it still doesn't switch to Coded PHY.
advParamLongRange.secPhy = GAP_ADV_SEC_PHY_CODED_S8;

// Create Advertisement set #2 and assign handle
status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLongRange,
&advHandleLongRange);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

// Load advertising data for set #2 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);

// Set event mask for set #2
status = GapAdv_setEventMask(advHandleLongRange,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);

// Enable long range advertising for set #2
status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
//Debug: CODED PHY, JL
Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Coded PHY Advertising Started with status: %d", status);

……

Question 1: From the code, it seems that two advertising sets are defined in simple_peripheral.c: one for legacy and one for long range. However, when I build and run the code, I always see "Adv Set 1 Enabled" in the CCS terminal, but I never see Adv Set 2 being enabled. In the SimplePeripheral_startAutoPhyChange function, I noticed a comment saying "//Set Default PHY to 1M", and I found the SimplePeripheral_addConn(uint16_t connHandle) function where I changed the default 1M setting connList[i].currPhy = HCI_PHY_1_MBPS; to connList[i].currPhy = HCI_PHY_CODED; but the terminal still shows “Adv Set 1 Enabled”.Could you explain why adv set 1 is always enabled? Also, in the SimplePeripheral_processCmdCompleteEvt function, I see the following code:

static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
{
uint8_t status = pMsg->pReturnParam[0];

//Find which command this command complete is for
switch (pMsg->cmdOpcode)
{
case HCI_READ_RSSI:
{
int8 rssi = (int8)pMsg->pReturnParam[3];

// Display RSSI value, if RSSI is higher than threshold, change to faster PHY
if (status == SUCCESS)
{
uint16_t handle = BUILD_UINT16(pMsg->pReturnParam[1], pMsg->pReturnParam[2]);

uint8_t index = SimplePeripheral_getConnIndex(handle);
SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS);

if (rssi != LL_RSSI_NOT_AVAILABLE)
{
connList[index].rssiArr[connList[index].rssiCntr++] = rssi;
connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH;

int16_t sum_rssi = 0;
for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++)
{
sum_rssi += connList[index].rssiArr[cnt];
}
connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH);

uint8_t phyRq = SP_PHY_NONE;
uint8_t phyRqS = SP_PHY_NONE;
uint8_t phyOpt = LL_PHY_OPT_NONE;

if(connList[index].phyCngRq == FALSE)
{
if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) &&
(connList[index].currPhy != HCI_PHY_2_MBPS) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to higher data rate
phyRqS = phyRq = HCI_PHY_2_MBPS;
}
else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) &&
(connList[index].rssiAvg >= RSSI_1M_THRSHLD) &&
(connList[index].currPhy != HCI_PHY_1_MBPS) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to legacy regular data rate
phyRqS = phyRq = HCI_PHY_1_MBPS;
}
else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) &&
(connList[index].rssiAvg < RSSI_1M_THRSHLD) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to lower data rate S=2(500kb/s)
phyRqS = HCI_PHY_CODED;
phyOpt = LL_PHY_OPT_S2;
phyRq = BLE5_CODED_S2_PHY;
}
else if(connList[index].rssiAvg < RSSI_S2_THRSHLD )
{
// try to go to lowest data rate S=8(125kb/s)
phyRqS = HCI_PHY_CODED;
phyOpt = LL_PHY_OPT_S8;
phyRq = BLE5_CODED_S8_PHY;
}
if((phyRq != SP_PHY_NONE) &&
// First check if the request for this phy change is already not honored then don't request for change
(((connList[index].rqPhy == phyRq) &&
(connList[index].phyRqFailCnt < 2)) ||
(connList[index].rqPhy != phyRq)))
{
//Initiate PHY change based on RSSI
SimplePeripheral_setPhy(connList[index].connHandle, 0,
phyRqS, phyRqS, phyOpt);
connList[index].phyCngRq = TRUE;

// If it a request for different phy than failed request, reset the count
if(connList[index].rqPhy != phyRq)
{
// then reset the request phy counter and requested phy
connList[index].phyRqFailCnt = 0;
}

if(phyOpt == LL_PHY_OPT_NONE)
{
connList[index].rqPhy = phyRq;
}
else if(phyOpt == LL_PHY_OPT_S2)
{
connList[index].rqPhy = BLE5_CODED_S2_PHY;
}
else
{
connList[index].rqPhy = BLE5_CODED_S8_PHY;
}

} // end of if ((phyRq != SP_PHY_NONE) && ...
} // end of if (connList[index].phyCngRq == FALSE)
} // end of if (rssi != LL_RSSI_NOT_AVAILABLE)

Display_printf(dispHandle, SP_ROW_RSSI, 0,
"RSSI:%d dBm, AVG RSSI:%d dBm",
(uint32_t)(rssi),
connList[index].rssiAvg);

} // end of if (status == SUCCESS)
break;
}

case HCI_LE_READ_PHY:
{
if (status == SUCCESS)
{
Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d",
pMsg->pReturnParam[3], pMsg->pReturnParam[4]);
}
break;
}

default:
break;
} // end of switch (pMsg->cmdOpcode)
}

Question 2: Based on the code mentioned above, I believe that the Simple Peripheral example is originally set to automatically switch to an appropriate PHY based on the RSSI value. Is that correct? If so, why does the signal only reach 15-20 meters during testing (in an open office environment with no obstructions or walls)? As I mentioned earlier, I only see “Adv Set 1 Enabled”. After connecting with the phone, I always see the terminal information: “PHY updated to 2M”, which comes from the SimplePeripheral_processStackMsg function, specifically in this Display_printf:

// A Phy Update Has Completed or Failed
if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
{
if (pPUC->status != SUCCESS)
{
Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
"PHY Change failure");
}
else
{
// Only symmetrical PHY is supported.
// rxPhy should be equal to txPhy.
Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
"PHY Updated to %s",
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
}

SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg);
}
break;

Therefore, I believe the device is capable of updating PHY. However, I am unsure why it fails to switch to the coded PHY when the transmission distance increases and instead disconnects. I have done some research, and there are mentions that Apple no longer supports coded PHY after iOS 13, which is why I borrowed my colleague's Samsung Galaxy S21 (Android 14, LightBlue App) for testing, but the results were the same.

Relevant links:

https://github.com/NordicSemiconductor/Android-BLE-Library/issues/166

https://forums.developer.apple.com/forums/thread/665542

https://issuetracker.google.com/issues/227887174

So, I would like to ask: does the code itself need further modification to support coded PHY, or is this a compatibility issue on the phone side? I am going to test on Samsung Galaxy S10 + Android 12 again, but I am not sure if I am in the correct direction. FYI, I didn't modify SimplePeripheral_processGapMessage, SimplePeripheral_processGATTMsg, SimplePeripheral_processStackMsg and SimplePeripheral_processCmdCompleteEvt. Could you provide some suggestions to help me achieve a long-range connection?

Thank you for your help!!! 

In addition, I have a problem using the BTool in the SDK, when I open the application(app and bat file), a message window popped up: "Device reset Timeout. Device might not function properly". 


Device: CC2640R2F LaunchBoard

SDK Version: simplelink_cc2640r2_sdk_5_30_00_03

  • Hi,

    Thank you for reaching out. To clarify, are you attempting to change the PHY for the advertisements or the PHY used in a connection? These are independent of one another.

    Best Regards,

    Jan

  • Hi Jan,

    Thanks for getting back to me promptly. If it’s not too complicated, I’d love to implement both long-range advertisement and connection. But let’s start with just the long-range connection for now. The launchboard is sending 12 bytes of ADC data once per second. Yesterday, I tried the nRF Connect App on a Samsung S21/Android 14. The range improved to around 30 meters, but it still didn’t meet my distance requirements. Could you guide me on how to modify the code to extend the range?

    Thank you so much !!!

    Jiawei 

  • Hi,

    If you have enabled BLE5 long range at simple peripheral then your smart phone should support BLE5 long range. Maybe the Samsung S21 has better antenna providing longer range.

    Another way is to use a range extender CC2640R2F + CC2592. But, that is custom hardware. I have this and tested the long range works.

    You can also try Sub 1 Ghz solutions.

    -kel

  • Hi Kel,

    Thank you for the suggestion. I don’t have much programming experience, so I’m not sure if I’ve successfully enabled the coded PHY connection in the code. Could you kindly let me know which functions in which files I should specifically check to confirm this? I wrote the code based on the BLE5 Simple Peripheral example. Since the maximum distance I could reach was only 30 meters, plus the CCS terminal window only showed "PHY updated to 2M," and the connection dropped due to the distance, it seems the coded PHY wasn’t used. I want first to verify whether the issue is in the code or with the testing device. Is the BLE5 Simple Peripheral example configured to enable the coded PHY connection by default? (Does it auto-change PHY according to RSSI?) Should I add something in the code to enable coded PHY connection?

    Best,

    Jiawei

  • Hi,

    Try changing the PHY using serial terminal menu. I have not tried it. The receiving end should also support the BLE5 long range example using another launchpad with simple central running. But if using smartphone from what I know you need to enable the BLE5 long range support using custom app.

    -kel

  • Try changing the PHY using serial terminal menu.

    - My BTool is also not working. The error message is attached. (at the end of my original question.) Can you look at it? I've reinstalled the newest SDK (simplelink_cc2640r2_sdk_5_30_01_11) but the issue persists: Device reset Timeout. Device might not function properly.

    The receiving end should also support the BLE5 long range example using another launchpad with simple central running. 

    - I will try. I have 2 launchboards.

    But if using smartphone from what I know you need to enable the BLE5 long range support using custom app.

    - I am using Lightblue App and nRF Connect App. I have tried on ios and android os.

    Thanks,

    Jiawei

  • My BTool is also not working. The error message is attached. (at the end of my original question.) Can you look at it? I've reinstalled the newest SDK (simplelink_cc2640r2_sdk_5_30_01_11) but the issue persists: Device reset Timeout. Device might not function properly.

    You need another launchpad with host test example program installed, to use the BTool.

    - I am using Lightblue App and nRF Connect App. I have tried on ios and android os.

    I am not sure if these apps automatically supports BLE5 long range. If it does you need to change settings somewhere.

    Here are training documentation regarding BLE5 long range. But it is not for CC2640R2F.

    https://dev.ti.com/tirex/content/cc13xx_cc26xx_simplelink_academy_7_41_00_00/_build_cc13xx_cc26xx_simplelink_academy_7_41_00_00/source/ble5stack/ble_phy/ble_phy.html

    -kel

  • Hi Kel,

    You need another launchpad with host test example program installed, to use the BTool.

    Ah! You hit the point! Now I realize that’s where the mistake was! Thank you so much for helping me clear this up. At least now I can use BTool to confirm the PHY settings, and then check the peripheral code using simple central. Thanks again! I’ll go try it out!