Tool/software:
Hi,
We are developing a BLE client Application. Could anyone help us to point to the correct example code?
Thanks and regards,
Ramya Gumaste
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.
Tool/software:
Hi,
We are developing a BLE client Application. Could anyone help us to point to the correct example code?
Thanks and regards,
Ramya Gumaste
Hello,
Unfortunately, we don't have any example code for GATT Client that doesn't use hardcoded values. However, we do plan on providing some guides soon, and I can post some code here to help you get started.
1. First discover the service that you're looking for. There are two APIs for accomplishing this, however, I will be showing one of them.
There are two APIs: GATT_DiscAllPrimaryServices and GATT_DiscPrimaryServiceByUUID. We will be using GATT_DiscPrimaryServiceByUUID for this example.
First, you need to decide when you want start discovering services. This could be as soon as the connection is established (which I will be covering here), or after bonding. I'm also assuming that you are using the basic_ble project, so we will be modifying some of the code within that project. Whether you decide to start after bonding, the process is the same, all that changes are the files that you modify (for instance, instead of app_connection.c, you would be modifying app_pairing.c).
In app_connection.c :: Connection_ConnEventHandler :: BLEAPPUTIL_LINK_ESTABLISHED_EVENT you will want to call GATT_DiscPrimaryServiceByUUID with the UUID of service you are looking for.
void Connection_ConnEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData) { switch(event) { case BLEAPPUTIL_LINK_ESTABLISHED_EVENT: { gapEstLinkReqEvent_t *gapEstMsg = (gapEstLinkReqEvent_t *)pMsgData; // Add the connection to the connected device list Connection_addConnInfo(gapEstMsg->connectionHandle, gapEstMsg->devAddr); /*! Print the peer address and connection handle number */ MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Established - " "Connected to " MENU_MODULE_COLOR_YELLOW "%s " MENU_MODULE_COLOR_RESET "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET, BLEAppUtil_convertBdAddr2Str(gapEstMsg->devAddr), gapEstMsg->connectionHandle); /*! Print the number of current connections */ MenuModule_printf(APP_MENU_NUM_CONNS, 0, "Connections number: " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET, linkDB_NumActive()); /* SimpleLink Service **/ uint8_t uuid[2] = {0xF0, 0xFF}; /** GATT Service Discovery **/ bStatus_t bstatus = GATT_DiscPrimaryServiceByUUID(gapEstMsg->connectionHandle, uuid, 2, BLEAppUtil_getSelfEntity()); if (bstatus != SUCCESS) { /* Place fail condition here */ ; } break; }
Taking a look at this specific API (link above), we are expecting an ATT_FIND_BY_TYPE_VALUE_RSP event that contains the attribute start and end handles for this particular service.
2. Handling ATT_FIND_BY_TYPE_VALUE_RSP
We now need to modify app_data.c :: dataGATTHandler struct and app_data.c :: GATT_EventHandler functions.
First, we need to add the event to our handler. This is done through the dataGATTHandler struct.
// Events handlers struct, contains the handlers and event masks // of the application data module BLEAppUtil_EventHandler_t dataGATTHandler = { .handlerType = BLEAPPUTIL_GATT_TYPE, .pEventHandler = GATT_EventHandler, .eventMask = BLEAPPUTIL_ATT_FLOW_CTRL_VIOLATED_EVENT | BLEAPPUTIL_ATT_MTU_UPDATED_EVENT | BLEAPPUTIL_ATT_FIND_BY_TYPE_VALUE_RSP | // This is the event we need to add. BLEAPPUTIL_ATT_READ_BY_TYPE_RSP // We will need this event as well, so add this now. };
Notice that I added two events here: BLEAPPUTIL_ATT_FIND_BY_TYPE_VALUE_RSP and BLEAPPUTIL_ATT_READ_BY_TYPE_RSP. The latter event is for when we want to discover the characteristics of a service. This will be covered in step 3, but I recommend adding this event now.
Once we have added the BLEAPPUTIL_ATT_FIND_BY_TYPE_VALUE_RSP event, we will need to handle it in app_data.c :: GATT_EventHandler
case ATT_FIND_BY_TYPE_VALUE_RSP: { uint16_t lastStart; uint16_t lastEnd; attFindByTypeValueRsp_t att = (attFindByTypeValueRsp_t)gattMsg->msg.findByTypeValueRsp; /* We need to build the 16-bit handles that we received when we discovered the service. */ for (uint8_t i = 0; i < att.numInfo; i++) { /* NOTE: We are in little endian, but the BUILD_UINT16 macro accounts for this */ /* The first two bytes will be start handle */ uint16_t attHandle = BUILD_UINT16(att.pHandlesInfo[i * 4], att.pHandlesInfo[i * 4 + 1]); /* The second two bytes will be the end handle */ uint16_t endHandle = BUILD_UINT16(att.pHandlesInfo[i * 4 + 2], att.pHandlesInfo[i * 4 + 3]); lastStart = attHandle; lastEnd = endHandle; MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0, "start: %x", attHandle); display_index++; MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0, "end: %x", endHandle); display_index++; } /* This delay can be replaced with a timer, or applicaton event that then calls GATT_DiscAllChars. * Without a delay, timer, or separate application event, this call will fail since the stack is busy * processing messages during this time. For the sake of the example, a delay was used, but * it does not have to be an explicit delay. */ ClockP_sleep(10); bStatus_t bstatus = GATT_DiscAllChars(gattMsg->connHandle, lastStart, lastEnd, BLEAppUtil_getSelfEntity()); if (bstatus != SUCCESS) { MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0,"FAILED!!!"); display_index++; } } break;
This will build our start and end handles for the specific service that we discovered. For brevity, the simplest method of accomplishing this was used. For instance, it would be better to store the start and end handles somewhere, so that you can keep track of them. Additionally, we use a delay here, but enqueuing a custom application event is better practice for kicking of characteristic discovery. I'd also like to note that the event in the switch statement drops the BLEAPPUTIL_* prefix.
We also introduce the API to discover characteristics, GATT_DiscAllChars. This API requires the ATT_READ_BY_TYPE_RSP, which is why we added BLEAPPUTIL_ATT_READ_BY_TYPE_RSP earlier.
3. Discovering characteristics for the service.
We now should discover the characteristics for the service you are querying.
case ATT_READ_BY_TYPE_RSP: { attReadByTypeRsp_t att = (attReadByTypeRsp_t)gattMsg->msg.readByTypeRsp; if (att.numPairs == 0) break; MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0, "ATT_READ_BY_TYPE_RSP"); display_index++; MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0, "Length: %d : Number of Pairs: %d", att.len, att.numPairs); display_index++; for (uint8_t i = 0; i < att.numPairs; i++) { /* This is the Attribute Handle */ uint16_t attHandle = BUILD_UINT16(att.pDataList[i * att.len + 0], att.pDataList[i * att.len + 1]); /* This is the index to the beginning of the value data AFTER the attribute handle */ uint8_t index = i * att.len + 2; /* The characteristic value data */ uint8_t char_properties = att.pDataList[index]; // Bit field of characteristic properties uint16_t value_handle = BUILD_UINT16(att.pDataList[index + 1], att.pDataList[index + 2]); MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0, " ATT Handle: %x : Value Handle %x : Properties : %x", attHandle, value_handle, char_properties); display_index++; if (att.len - 5 == 2) { uint16_t char_16bit_uuid = BUILD_UINT16(att.pDataList[index + 3], att.pDataList[index + 4]); MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0," UUID: %x", char_16bit_uuid); display_index++; } else { MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, 0," UUID: "); for (uint8_t j = 0; j < 16; j++) { MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE + display_index, j + 10, "%x", att.pDataList[index + 3 + j]); } display_index++; } } } break;
The above code will parse through the ATT_READ_BY_TYPE_RSP, and will provide the attribute handle, value handle, characteristic properties, and UUID of the characteristic. I recommend reading the ATT and GATT chapters of the Bluetooth Core Specification for more information how to parse this information. You can also refer to this E2E thread where I dig into the specification for more information. The response highlighted in green is relevant.
At this point, a service has been discovered, and the characteristics that make up that service have been discovered as well. It's up to the developer for what comes after this point (for instance, storing the handles, writing to the values, etc.).
Hope that helps, and please let me know if you have any further questions. Additionally, please refer to the API information TI BLE5-Stack API Documentation: ATT / GATT for more technical details.
Best,
Nima Behmanesh