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.

CC2652RB: Data transfer through BLE

Part Number: CC2652RB

Hi Team,

Need your expertise on our customer's query. Posting on their behalf:

I am using CC2652RB for my development project. But i am facing challenge in transferring Text file data to GATT client via BLE. As a beginning step, I'm trying to develop on project zero sample code and i have completed all steps of the Bluetooth low energy Custom Profile tutorial on this link dev.ti.com/.../ble_01_custom_profile.html
However I am not able to verify the functionality of Task 5 image attached. On pressing button the initial value is not getting updated as mentioned in the step 4 of task 5. Please refer to the attached image.
Can you help me where I need to make changes in project zero sample code to transfer data from GATT server (CC2652RB launchpad) to GATT client (Android smart phone) via BLE after a button push as suggested in the link?
Right now what is happening is, I will first write some data from android phone in the sunlight service characteristic and then after a button press I am able to read data on the sunlight service characteristic.
My question is why the initial value is not getting updated as I have called sunlight service set parameter function in handle button press and second is i just want keep updating it every time button is pressed but its stuck at the initial value.

Please also refer the attached PDF file for my current state of task. Thanks in advance!


Kind Regards,

Jejomar

TI Ble current state.pdf

  • Hi Jejomar,

    From my understanding the characteristic is not read again by the phone, as a result the phone does not refresh the display. Please try to read the value again.

    If you does not want to re-read the value then you have to enable notifications.

    All the elements I mentioned should be one on the mobile application.

    Let us know if it helps,

    Regards,

  • Hello Clément,

    Many thanks for looking into this. Our customer does not want to re-read the characteristics and they'd like to get it updated after pressing the button. Can you guide us on how to enable notifications?

    Also is it possible to transmit more and more data without button press? i.e. they want to transmit all the contents of text file by single button press. The file will have approximately 100 kB or more data i.e. complete data should be transmitted till the pointer reaches the end of file.


    Kind Regards,

    Jejomar

  • Hi Jejomar,

    It is not clear to me, are you trying to display an additional characteristic in the mobile application? Could you provide some screenshots?

    Jejomar Ildefonso said:
    The file will have approximately 100 kB or more data i.e. complete data should be transmitted till the pointer reaches the end of file.

    This looks possible but will require some work. You may want to review how OAD images are transmitted and use a similar solution.

    Regards,

  • Hello Clément,

    There are two particular questions from our customer:

    First they'd like to know how to enable notifications in the project zero code, so that upon pressing the left button the value should increase. Please refer to the attached pdf file in the start of the thread for the screenshots.

    Second, for the OAD transfer they will be referring to that procedure. But in the project zero code it is given that it is possible to transfer large data using GATT ReadLongCharValue or GATT WriteLongCharValue. Can they use the same procedure? Can they just have to read the contents of the file and transmit character by character?

    We have put the screenshots of the app side (GATT client) into the attached pdf. In that tutorial, they are creating a custom profile with a new service and characteristic. Please refer to the screenshot and let me know if you need more information.


    Kind Regards,

    Jejomar

  • Hi Jejomar,

    Based on the screen shots provided, the notifications are not enabled on the characteristic you are interested in. Could you try to enable them as you did for the other characteristic?

    I am sorry I am not an expert of the mobile applications - rather of the CC26xx devices.

    GATT_WriteLongCharValue can be used when the length of the Characteristic Value is longer than can be sent in a single Write Request Attribute Protocol message. So it looks definitely usable in your case.

    Best regards,

  • Hello Clément,

    My apologies for the delay and many thanks for continuously helping us with this query. Upon sharing your latest response I have received a feedback from the customer as follows:

    When i am enabling notifications for the second characteristic then also the initial value is not getting updated. Could you tell me how to enable the notifications from CC26xx side. I want to work on the cc26xx side code only.
    GATT_WriteLongCharValue where do I have to implement this?
    I have also gone through OAD but unable to understand it for my requirement. Is it possible to transfer all file content by single button push through project zero code?


    Kind Regards,

    Jejomar



  • Hi Jejomar,

    The notifications should be turned on on the GATT client's side (i.e. the phone). The only think that should be done on the GATT server's side is to verify if the GATT characteristics can be "notified". In addition to that, you should verify how the data are sent by the GATT serer - please review the SimpleLink Academy for additional support.

    From my understanding, the goal here is to send the files from the phone to the CC26x2 device. It means the GATT_WriteLongCharValue function probably won't be used.

    Is it possible to transfer all file content by single button push through project zero code?

    Yes, I think so. Mobile app experts will probably have better insights than me.

    Regards,

  • Hello Clément,

    Thank you for your response. I've shared these to our customer and he responded that the end goal is to transfer the contents of .txt file stored in SD card interfaced with TICC2652RB LP to the mobile phone (Android-based). Either send the complete file in one go or read the contents of the file and transmit character by character.

    It seems that the development has to happen from the TI CC2652 side (GATT server in the customer's case), not from the android mobile side.
    How can they check whether the characteristic can be notified or not and where in the project zero do they need to verify for custom ble profile?

    Secondly, if a gatt characteristic is not set to "can be notified". Can we change its notification properties?

    Thanks in advance in continuously helping us!


    Kind Regards,

    Jejomar

  • Hi Jejomar,

    Some development will be required on both sides.

    I would recommend to leverage the simple_peripheral example (rather than the project_zero example). As you will see in the README file of the example, the simple_peripheral example provides characteristics with possibility of notification.

    I recommend using LightBlue application on the phone side to evaluate the simple_peripheral example.

    Best regards,

  • Hello Clément,

    Many thanks. Our customer confirmed that they will indeed go through these peripheral examples. Their main query is concerning the project zero code as they've already explored it in a decent level and hoping that it would work.

    If it is possible, can you help us look into the code of project zero and the project zero custom ble code and let us know if it's possible to update the sensor value by button press as mentioned in the simple link tutorial?

    It would be great and very helpful for our customer if you can help us in this specific case.


    Kind Regards,

    Jejomar

  • Hi Jejomar,

    I confirm this is possible.

    Regards,

  • Hello Clément,

    Apologies for the delay and thank you for the confirmation. However, this seems to be not happening on the case of our customer. I'm attaching their code.

    In the code, the value is autoincrementing for our custom characteristic with UUID (F0002BAD-0451-4000-B000-000000000000) for custom service (0000BA55-0000-1000-8000-00805F9B34FB) whereas according to the simple link custom BLE Profile tutorial, it should increase for another custom characteristic of custom service when we are initializing the custom characteristic with a value.
    Can you help us look into the code? It seems that there is some wrong sequence of callback.

    We really appreciate your continuous support!


    Kind Regards,

    Jejomar

    project_zero.c.txt

    sunlightService.c.txt
    /**********************************************************************************************
     * Filename:       sunlightService.c
     *
     * Description:    This file contains the implementation of the service.
     *
     * Copyright (c) 2015-2019, 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.
     *
     *************************************************************************************************/
    
    
    /*********************************************************************
     * INCLUDES
     */
    #include <string.h>
    
    #include <icall.h>
    
    /* This Header file contains all BLE API and icall structure definition */
    #include "icall_ble_api.h"
    
    #include "sunlightService.h"
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    /*********************************************************************
    * GLOBAL VARIABLES
    */
    
    // sunlightService Service UUID
    CONST uint8_t sunlightServiceUUID[ATT_BT_UUID_SIZE] =
    {
      LO_UINT16(SUNLIGHTSERVICE_SERV_UUID), HI_UINT16(SUNLIGHTSERVICE_SERV_UUID)
    };
    
    // sunlightValue UUID
    CONST uint8_t sunlightService_SunlightValueUUID[ATT_UUID_SIZE] =
    {
      TI_BASE_UUID_128(SUNLIGHTSERVICE_SUNLIGHTVALUE_UUID)
    };
    // updatePeriod UUID
    CONST uint8_t sunlightService_UpdatePeriodUUID[ATT_UUID_SIZE] =
    {
      TI_BASE_UUID_128(SUNLIGHTSERVICE_UPDATEPERIOD_UUID)
    };
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    static sunlightServiceCBs_t *pAppCBs = NULL;
    
    /*********************************************************************
    * Profile Attributes - variables
    */
    
    // Service declaration
    static CONST gattAttrType_t sunlightServiceDecl = { ATT_BT_UUID_SIZE, sunlightServiceUUID };
    
    // Characteristic "SunlightValue" Properties (for declaration)
    static uint8_t sunlightService_SunlightValueProps = GATT_PROP_NOTIFY;
    
    // Characteristic "SunlightValue" Value variable
    static uint8_t sunlightService_SunlightValueVal[SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN] = {0};
    
    // Characteristic "SunlightValue" CCCD
    static gattCharCfg_t *sunlightService_SunlightValueConfig;
    // Characteristic "UpdatePeriod" Properties (for declaration)
    static uint8_t sunlightService_UpdatePeriodProps = GATT_PROP_READ | GATT_PROP_WRITE;
    
    // Characteristic "UpdatePeriod" Value variable
    static uint8_t sunlightService_UpdatePeriodVal[SUNLIGHTSERVICE_UPDATEPERIOD_LEN] = {0};
    
    /*********************************************************************
    * Profile Attributes - Table
    */
    
    static gattAttribute_t sunlightServiceAttrTbl[] =
    {
      // sunlightService Service Declaration
      {
        { ATT_BT_UUID_SIZE, primaryServiceUUID },
        GATT_PERMIT_READ,
        0,
        (uint8_t *)&sunlightServiceDecl
      },
        // SunlightValue Characteristic Declaration
        {
          { ATT_BT_UUID_SIZE, characterUUID },
          GATT_PERMIT_READ,
          0,
          &sunlightService_SunlightValueProps
        },
          // SunlightValue Characteristic Value
          {
            { ATT_UUID_SIZE, sunlightService_SunlightValueUUID },
            GATT_PERMIT_READ,
            0,
            sunlightService_SunlightValueVal
          },
          // SunlightValue CCCD
          {
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE,
            0,
            (uint8 *)&sunlightService_SunlightValueConfig
          },
        // UpdatePeriod Characteristic Declaration
        {
          { ATT_BT_UUID_SIZE, characterUUID },
          GATT_PERMIT_READ,
          0,
          &sunlightService_UpdatePeriodProps
        },
          // UpdatePeriod Characteristic Value
          {
            { ATT_UUID_SIZE, sunlightService_UpdatePeriodUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE,
            0,
            sunlightService_UpdatePeriodVal
          },
    };
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    static bStatus_t sunlightService_ReadAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                               uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                               uint16_t maxLen, uint8_t method );
    static bStatus_t sunlightService_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                                uint8_t *pValue, uint16_t len, uint16_t offset,
                                                uint8_t method );
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    // Simple Profile Service Callbacks
    CONST gattServiceCBs_t sunlightServiceCBs =
    {
      sunlightService_ReadAttrCB,  // Read callback function pointer
      sunlightService_WriteAttrCB, // Write callback function pointer
      NULL                       // Authorization callback function pointer
    };
    
    /*********************************************************************
    * PUBLIC FUNCTIONS
    */
    
    /*
     * SunlightService_AddService- Initializes the SunlightService service by registering
     *          GATT attributes with the GATT server.
     *
     */
    extern bStatus_t SunlightService_AddService( uint8_t rspTaskId )
    {
      uint8_t status;
    
      // Allocate Client Characteristic Configuration table
      sunlightService_SunlightValueConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns );
      if ( sunlightService_SunlightValueConfig == NULL )
      {
        return ( bleMemAllocError );
      }
    
      // Initialize Client Characteristic Configuration attributes
      GATTServApp_InitCharCfg( LINKDB_CONNHANDLE_INVALID, sunlightService_SunlightValueConfig );
      // Register GATT attribute list and CBs with GATT Server App
      status = GATTServApp_RegisterService( sunlightServiceAttrTbl,
                                            GATT_NUM_ATTRS( sunlightServiceAttrTbl ),
                                            GATT_MAX_ENCRYPT_KEY_SIZE,
                                            &sunlightServiceCBs );
    
      return ( status );
    }
    
    /*
     * SunlightService_RegisterAppCBs - Registers the application callback function.
     *                    Only call this function once.
     *
     *    appCallbacks - pointer to application callbacks.
     */
    bStatus_t SunlightService_RegisterAppCBs( sunlightServiceCBs_t *appCallbacks )
    {
      if ( appCallbacks )
      {
        pAppCBs = appCallbacks;
    
        return ( SUCCESS );
      }
      else
      {
        return ( bleAlreadyInRequestedMode );
      }
    }
    
    /*
     * SunlightService_SetParameter - Set a SunlightService parameter.
     *
     *    param - Profile parameter ID
     *    len - length of data to right
     *    value - pointer to data to write.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate
     *          data type (example: data type of uint16 will be cast to
     *          uint16 pointer).
     */
    bStatus_t SunlightService_SetParameter( uint8_t param, uint16_t len, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        case SUNLIGHTSERVICE_SUNLIGHTVALUE_ID:
          if ( len == SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN )
          {
            memcpy(sunlightService_SunlightValueVal, value, len);
    
            // Try to send notification.
            GATTServApp_ProcessCharCfg( sunlightService_SunlightValueConfig, (uint8_t *)&sunlightService_SunlightValueVal, FALSE,
                                        sunlightServiceAttrTbl, GATT_NUM_ATTRS( sunlightServiceAttrTbl ),
                                        INVALID_TASK_ID,  sunlightService_ReadAttrCB);
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        case SUNLIGHTSERVICE_UPDATEPERIOD_ID:
          if ( len == SUNLIGHTSERVICE_UPDATEPERIOD_LEN )
          {
            memcpy(sunlightService_UpdatePeriodVal, value, len);
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      return ret;
    }
    
    
    /*
     * SunlightService_GetParameter - Get a SunlightService parameter.
     *
     *    param - Profile parameter ID
     *    value - pointer to data to write.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate
     *          data type (example: data type of uint16 will be cast to
     *          uint16 pointer).
     */
    bStatus_t SunlightService_GetParameter( uint8_t param, uint16_t *len, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
          case SUNLIGHTSERVICE_SUNLIGHTVALUE_ID:
            if ( len == SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN )
            {
              memcpy(sunlightService_SunlightValueVal, value, len);
    
              // Try to send notification.
              GATTServApp_ProcessCharCfg( sunlightService_SunlightValueConfig, (uint8_t *)&sunlightService_SunlightValueVal, FALSE,
                                          sunlightServiceAttrTbl, GATT_NUM_ATTRS( sunlightServiceAttrTbl ),
                                          INVALID_TASK_ID,  sunlightService_ReadAttrCB);
            }
            else
            {
              ret = bleInvalidRange;
            }
            break;
        case SUNLIGHTSERVICE_UPDATEPERIOD_ID:
          memcpy(value, sunlightService_UpdatePeriodVal, SUNLIGHTSERVICE_UPDATEPERIOD_LEN);
          break;
    
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      return ret;
    }
    
    
    /*********************************************************************
     * @fn          sunlightService_ReadAttrCB
     *
     * @brief       Read an attribute.
     *
     * @param       connHandle - connection message was received on
     * @param       pAttr - pointer to attribute
     * @param       pValue - pointer to data to be read
     * @param       pLen - length of data to be read
     * @param       offset - offset of the first octet to be read
     * @param       maxLen - maximum length of data to be read
     * @param       method - type of read message
     *
     * @return      SUCCESS, blePending or Failure
     */
    static bStatus_t sunlightService_ReadAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                           uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                           uint16_t maxLen, uint8_t method )
    {
      bStatus_t status = SUCCESS;
    
      // See if request is regarding the SunlightValue Characteristic Value
    if ( ! memcmp(pAttr->type.uuid, sunlightService_SunlightValueUUID, pAttr->type.len) )
      {
        if ( offset > SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN )  // Prevent malicious ATT ReadBlob offsets.
        {
          status = ATT_ERR_INVALID_OFFSET;
        }
        else
        {
          *pLen = MIN(maxLen, SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN - offset);  // Transmit as much as possible
          memcpy(pValue, pAttr->pValue + offset, *pLen);
        }
      }
      // See if request is regarding the UpdatePeriod Characteristic Value
    else if ( ! memcmp(pAttr->type.uuid, sunlightService_UpdatePeriodUUID, pAttr->type.len) )
      {
        if ( offset > SUNLIGHTSERVICE_UPDATEPERIOD_LEN )  // Prevent malicious ATT ReadBlob offsets.
        {
          status = ATT_ERR_INVALID_OFFSET;
        }
        else
        {
          *pLen = MIN(maxLen, SUNLIGHTSERVICE_UPDATEPERIOD_LEN - offset);  // Transmit as much as possible
          memcpy(pValue, pAttr->pValue + offset, *pLen);
        }
      }
      else
      {
        // If we get here, that means you've forgotten to add an if clause for a
        // characteristic value attribute in the attribute table that has READ permissions.
        *pLen = 0;
        status = ATT_ERR_ATTR_NOT_FOUND;
      }
    
      return status;
    }
    
    
    /*********************************************************************
     * @fn      sunlightService_WriteAttrCB
     *
     * @brief   Validate attribute data prior to a write operation
     *
     * @param   connHandle - connection message was received on
     * @param   pAttr - pointer to attribute
     * @param   pValue - pointer to data to be written
     * @param   len - length of data
     * @param   offset - offset of the first octet to be written
     * @param   method - type of write message
     *
     * @return  SUCCESS, blePending or Failure
     */
    static bStatus_t sunlightService_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                            uint8_t *pValue, uint16_t len, uint16_t offset,
                                            uint8_t method )
    {
      bStatus_t status  = SUCCESS;
      uint8_t   paramID = 0xFF;
    
      // See if request is regarding a Client Characterisic Configuration
      if ( ! memcmp(pAttr->type.uuid, clientCharCfgUUID, pAttr->type.len) )
      {
        // Allow only notifications.
        status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                 offset, GATT_CLIENT_CFG_NOTIFY);
      }
      // See if request is regarding the UpdatePeriod Characteristic Value
      else if ( ! memcmp(pAttr->type.uuid, sunlightService_UpdatePeriodUUID, pAttr->type.len) )
      {
        if ( offset + len > SUNLIGHTSERVICE_UPDATEPERIOD_LEN )
        {
          status = ATT_ERR_INVALID_OFFSET;
        }
        else
        {
          // Copy pValue into the variable we point to from the attribute table.
          memcpy(pAttr->pValue + offset, pValue, len);
    
          // Only notify application if entire expected value is written
          if ( offset + len == SUNLIGHTSERVICE_UPDATEPERIOD_LEN)
            paramID = SUNLIGHTSERVICE_UPDATEPERIOD_ID;
        }
      }
      else
      {
        // If we get here, that means you've forgotten to add an if clause for a
        // characteristic value attribute in the attribute table that has WRITE permissions.
        status = ATT_ERR_ATTR_NOT_FOUND;
      }
    
      // Let the application know something changed (if it did) by using the
      // callback it registered earlier (if it did).
      if (paramID != 0xFF)
        if ( pAppCBs && pAppCBs->pfnChangeCb )
          pAppCBs->pfnChangeCb(connHandle, paramID, len, pValue); // Call app function from stack task context.
    
      return status;
    }
    

  • Hi,

    What is the value returned by GATTServApp_ProcessCharCfg()? Have you tried to step in the function to verify everything is as expected?

    Regards,

  • Hello Clément,

    Apologies. I've just received an update from our customer's side, now for the custom characteristic of sunlight characteristic as mentioned in the custom BLE profile tutorial. The value is not autoincrementing , it is incrementing only when they are pressing the button after enabling the notifications. So the previous problem is solved but we're really not quite sure whether this is the right way to solve the problem.

    The main task is to read the contents of the text file stored in SD Card on button press and transmit them character by character via BLE.
    So the plan to go about it is as follows:
    They will do the development on sunlight characteristic code and first assign it a variable that reads the ADC value from a GPIO pin. This value will be given to myVar. Then every time when they will have to read it, they will press the button then the value should be transmitted to android app assuming the notifications are enabled from the GATT Client.

    Secondly for reading the data from SD card, do they also have to give the GATT Permit read and write permissions along with the notifications? Or only the notification enabled from the android side (GATT Client) option is sufficient? After enabling the notifications they can continuously receive the contents of the data card.

    We really appreciate your continuous support. This would the last step for our customer's task and hope you can help us. Thanks!


    Kind Regards,

    Jejomar

  • Hi Jejomar,

    I understand the initial issues are now solved. So please close this thread and open a new one - that way we avoid having too long thread an missing information.

    Thank you for your comprehension.

    Best regards,