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.

LAUNCHXL-CC2640R2: Receiving BLE Data With Customer Profile

Part Number: LAUNCHXL-CC2640R2
Other Parts Discussed in Thread: CC2640R2F, LAUNCHXL-F28069M, CC2652RB

I am currently using SimplePeripheral and SimpleCentral programs to try and send joystick data through ble.  I can get a connection and send the data from the peripheral after following the tutorials, creating a service, and calling the MyData_SetParameter that is created following the Bluetooth Low Energey + TI Drivers tutorial.  I created another identical service and put it in the SimpleCentral program.  When a connection is established, I create a thread that uses a clock and semaphore and every time the semaphore posts I try to read the value with my service.  I am not sure if I'm using the correct function.  I assumed that I would use the MyData_GetParameter function, but that does not seem to be getting the value that is being sent.  Is this the correct function or should I be using the 

myData_ReadAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                      uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                      uint16_t maxLen, uint8_t method );

It's been a minute since using C so please forgive the nubie questions.  I would assume the myData_ReadAttrCB is not available as it is in the .c file. 

Task_FuncPtr *readAttr_thread(void *arg0) {

     Semaphore_Params semParams;
     Semaphore_Params_init(&semParams);

     semHandle = Semaphore_create(0, &semParams, Error_IGNORE);

     if(semHandle == NULL)
     {
           /* Semaphore_create() failed */
         Display_print0(dispHandle, 0, 0, "semHandle Semaphore creation failed\n");
         while (1);
     }

     Clock_Params clkParams;

     Clock_Params_init(&clkParams);
     clkParams.period = 5000/Clock_tickPeriod;
     clkParams.startFlag = FALSE;

     /* Construct a periodic Clock Instance */
     Clock_construct(&clkStruct, (Clock_FuncPtr)clock_Handler,
                                     5000/Clock_tickPeriod, &clkParams);

     clkHandle = Clock_handle(&clkStruct);
     Clock_start(clkHandle);


    while (1) {
        /* Pend on semaphore, tmp116Sem */
        Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);

//        myData_ReadAttrCB(connHandle,
//                          myDataAttrTbl,
//                          bleReadValue,
//                          MYDATA_DATA_LEN,
//                          0,
//                          MYDATA_DATA_LEN,
//                          GATT_CLIENT_CFG_NOTIFY );
        /* Blocking mode conversion */
        MyData_GetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN, &bleReadValue);
        Display_printf(dispHandle, 6, 0, "ADC value read from ble: %d\n", bleReadValue);
        Clock_start(clkHandle);
    }
}


void bleRead_thread(void) {
    Task_Params taskParams;

    // Configure task
    Task_Params_init(&taskParams);
    taskParams.stack = myTaskStack;
    taskParams.stackSize = THREADSTACKSIZE;
    taskParams.priority = 1;

   Task_construct(&myTask, readAttr_thread, &taskParams, Error_IGNORE);
}

  • Hello Charlie,

    Could you please specify what SDK are you using?

    I would suggest using the myData_ReadAttrCB() function as it allows you to specify the attribute you are reading from the peripheral which is shared among both services.

    I think this example will be very useful for you to use as a reference.

    Please let me know if it helps.

    David.

  • Hey David. Thank you for the reference to the repo.  I'm trying to view the code from that repo while also trying to draw parallels to the tutorials supplied by TI that I've been following. After reading through the Custom Profile tutorial again, it seems as though the functions MyData_SetParameter and MyData_GetParameter are used to get values that are written to the gatt server and the myData_ReadAttrCB() and myData_WriteAttrCB() are callbacks for when the set and get parameters are used? Is my thinking correct? And if this is the case, it seems as though I could just call MyData_GetParameter from my simpleCentral program once a connection is made.  However, I'm not seeing any values being read so maybe I'm not quite correct. 
    The Bluetooth Low Energy + TI Drivers demos how to send bluetooth data and it just says to use the MyData_SetParameter to actually send the data.  So that is why I originally thought that I would just use MyData_GetParameter to get the value on the other side.  Although this does seem maybe not quite correct as though you are not specifying which attribute you want to read.  Although there is only one attribute in my attribute table there could obviously be many. However, in the generated service that I copied code from in the Custom Profile tutorial the CB functions are not listed in the header files for the classes so they are not callable in other files. 

    I read through those tutorials linked above, but I think maybe I am still a little unclear about how this all works. I have been stuck on this for a while now and am just trying to get some simple joystick data to be sent between two boards so I can progress and move forward with the project, but this has proven to be a difficult to me. Any help getting me over this hump is much appreciated.

  • Hello Charlie,

    I think the main issue here is that your new task is not ICall registered, meaning that your RTOS task can not communicate with the BLE5-Stack as they are not executing in the same context. I would suggest taking a look at the User Guide here (under the section Creating Additional ICall Enabled Tasks).

    Let me know how it goes.

    BR,

    David.

  • it seems as though I could just call MyData_GetParameter from my simpleCentral program once a connection is made

    You need to understand how the original simple peripheral and simple central works. Then modify it from there. I do not think your implementation will work at simple central side. I suggest you test first using smartphone IOS LightBlue App or Android nRF Connect to see if you are getting joystick data.

    Here is a project I did using simple peripheral as base. It is a Bluetooth game controller using CC2640R2F Launchpad.

    -kel

  • I did use Lightblue with my iphone and I am getting joystick data read.

  • I have been trying to look at the example that you referenced in the github repo.  However, I am unsure how to run the programs.  In the readMe for the link it says to just run simple_serial_socket_client project and stack files, but when I clone the repo they aren't buildable or runnable they seem.  Am I missing something?  

  • Hello Charlie,

    For this examples you would have to: 1) download the entire examples repository, 2) when trying to import the project, select the entire ble_example folder and select the _app directory (it will also load the stack_library related directory).

    Did you check that your new task is ICall registered?

    On a different subject, have you thought about using a newer device? We have an example for HID Over GATT Profile (HOGP), also known as HID over BLE for CC26X2 devices.

    BR,


    David

  • I looked at the documentation Creating Additional ICall Enabled Tasks and tried to add the  ICall_registerApp to my task function, but still no luck. 

    I saw also that it talked about adding Event_pend, but that doesn't make any sense as I am not registering any events.  So my code calls this function above when there is a connection.  And it is using a function with a semaphore to continually try and get the characteristic value


    I know my Simple_Peripheral is sending joystick information as I connected it to the LightBlue App and was hitting read on the characteristic. And I would move the joystick and hit read again on LightBlue and it would show the exact same reading as the joystick value.

    So I basically followed the tutorial on the Bluetooth Low Energy + TI Drivers on the sdk for cc2640R2.  And if you look at that tutorial, you will be able to see the exact service that I made for the simple peripheral.  And I just use a task with semaphores as well and continually call MyData_SetParameter() which continually passes the ADC value of the joystick.  


    I then just copied the service exactly and put it in the Simple Central.  However, I then modified the MyData_GetParameter() function 

    bStatus_t MyData_GetParameter( uint8_t param, uint16_t* len, void *value )
    {
      bStatus_t ret = SUCCESS;
    
      switch ( param )
      {
        case MYDATA_DATA_ID:
           memcpy(value, myData_DataVal, MYDATA_DATA_LEN);
           break;
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      return ret;
    }

    .  

    But I still can not get a reading.  I opened a terminal and print out the bleRead value as in the readAttr_thread() function, but it just says 0.  

    I also tried to pull in the socket_server and socket_client examples that you showed me, but was having issues as it said there were files missing.  And I could not see them anywhere in the sdk to try and pull them in. 

      


    Maybe I will just have to try and create a brand new CCS project and try and copy that code into certain files.  I don't know.  Any continued help to figure this out would be great. If you would be up for it, maybe we could do a google hangout or something. Thanks for the help

  • Hello Charlie,

    Please help me with the following questions:

    1. May I ask what are the steps you follow to import the files? and what ble SDK are you using?
    2. Is there a specific reason to why you are not using the peripheral to send data through notifications?
    3. Is the idea that your application on the central side will read data constantly from the peripheral through read requests?

    BR,

    David.

  • Hey David,

    1.  Looks like I was using the latest sdk 5.30.00.03. That's why I was not seeing the files.   I have successful loaded the example and it is working according         to the read me.  I can type in the terminals and it will appear in the other terminal.  

    2. Yes so the purpose of this project is for one of the bluetooth boards to be attached to joystick data and it will continually send the joystick data.  The other        board will receive the joystick data continuously and then it will be connected via UART to a LaunchXL-F28069M with two motor drivers on it. And that              joystick data will be converted into how fast the motors will spin.  Is this the correct case for sending data continuously like I intend or should I still send it          via notifications?

    3. Yes.

    So I guess at this point I should try and maybe look at the example that I finally got working in the sdk3 with the simple_socket_server and simple_socket_client and see if I can get my simple_central receiving the data?  I know for a fact that my simple peripheral is sending joystick data and it is being received as I used the light blue app and could read the joystick data from the app.  I'm just having the issue of reading that data in the simple_central. There is of course a connection between the two boards by pressing the different buttons.  So I know there is a connection.  It's just something small like the ICall or somehow they are not on the same frequency.  

    Maybe it will be easier if you could look at my code.  

    Here are the PROFILE directory, the Application directory, and the Startup directory of my Simple Peripheral and Simple Central programs.  That should have all the necessary files that you should be able to see my code.  

    Simple Peripheral

    https://github.com/cbarb15/Simple-Peripheral/tree/main

    Simple Central

    github.com/.../Simple-Central

    The code that I was attempting to read the joystick data from is called readAttr_thread() in the simple_central.c file

    The files that create the services for each project are in the Profiles directories and are both called myData.h and myData.c

  • Hello Charlie,

    1. Glad to hear the examples are working now.
    2. If your peripheral device is acquiring the joystick data then you would have to constantly do read requests from the central to fetch the data. The other option is to send notification from the peripheral every time there is new joystick data because the central will not be aware of that. How much data are you sending?
    3. I will take a look at the code and see how I can help.

    BR,

    David.

  • Hey David,

    I'm not sending much data.  It's just the uint16_t adc values.

    Ok thanks for taking a look.  

  • Hey David,

        So I went back and read through the BLE Stack User Guide for the CC2640R2, specifically the part on the GATT and I was definitely thinking about the peripheral/central and GATT server/GATT client in the wrong way.  After reading the part about the GATT, it was clear that the 

    "The GATT roles of client and server are independent from the GAP roles of peripheral and central. A peripheral can be either a GATT client or a GATT server, and a central can be either a GATT client or a GATT server. A peripheral can act as both a GATT client and a GATT server"

    Which in my case, the simplePeripheral is acting as the GATT server and the simpleCentral is acting as the GATT client. Also it was made more clear that the GATT server is the only one with the service and attribute table.  From what I understood, I can just use the GATT layer directly and make read or write requests.  So I changed the code in the simpleCentral task to constantly call GATT_ReadCharValue() in order to make a read request.  However, I am still not seeing the correct data. It seems like my code should work according to the GATT description linked above and the API call GATT_ReadCharValue().  In the description for the function call it says that

    If the return status from this function is SUCCESS, the calling application task will receive a GATT_MSG_EVENT message with method. 

    I am getting a successful read and I would assume that the code would go to the SimpleCentral_processStackMsg() in simpleCentral.c.  I did put a global variable that I could set equal to a local variable inside the function so I could view the value of it in the expressions of code composer.  I'm seeing 



    Which the value just seems to be nonsense.  But above this code, when making the read request, the readStatus variable comes back with a 0x00 value which according to the documentation is a success. 
    .  

    I've updated the GitHub repo with the new code.  The areas of interest in 

    Simple Central GitHub

    PROFILES/myData.c   This is the service

    Application/myThread.c  This is where I send the joystick data

    Simple Peripheral GitHub

    Application/simple_central.c

    bleRead_thread() line 952 - 964

    readAttr_thread() line 908 - 949

    Line 943 I call the GATT_ReadCharValue() which in turn should go to the SimpleCentral_processStackMsg() I would think if the read request is successful.  

  • Hello Charlie,

    Could you please help me double checking the following:

    1. The readStatus value is initialized before calling GATT_ReadCharValue() as 0x00 or is it actually updating to 0x00 after gatt read function?
    2. Inside SimpleCentral_processStackMsg(), if you set a break-point, it is entering to the GATT_MSG_EVENT case (which will later call the SimpleCentral_processGATTMsg function) ? What is the value of pMsg->method (ATT_READ_RSP, ATT_ERROR_RSP)?
    3. Are you reading the correct service and attribute established by the server?

    BR,

    David.

  • Hi,

    The original simple peripheral and simple central can write 1 byte to characteristic 4 if I remember it correctly. So, just increase it to 2 bytes and then your problem is solved.

    Although, I prefer to just create a custom BLE service. The problem is modifying the simple central side. Actually, I have done it several times already so I am familiar.

    The simple serial socket should also work. But, I think the power consumption will be higher.

    -kel

  • Hey David, 

    So I tried to simplify the problem and instead of trying to read from a thread continuously, I just called GATT_ReadCharValue immediately after the connection was established. 

    1. It looks like readStatus is 0x00 before  the call to GATT_ReadCharValue

    2. Inside SimpleCentral_processStackMsg() it first hit the successful read else block and then immediately hit the error block and it say Read Error 1 in the serial terminal. I tried to find what error 1 is, but am having an issue finding the meaning of it in the documentation.  


    3. I think I am reading the correct service and attribute.  But maybe I am not if there is a read error. 

  • I'll look at the simpleChar4.  Yeah I was wanting to create my own custom BLE service to understand it.  And I followed the custom profile tutorial and am able to send data just fine from simple_peripheral.  I can verify it is sending the data using the LightBlue app.  I'm just having an issue reading the data from simple_central.  It seems as though it should be as simple as calling GATT_ReadCharValue after a connection is made, but that has not been my experience.   

  • Hi,

    https://github.com/mtrobregado/TI-CC2652RB-BLE-Point-Of-Sale

    Reviewing my project above might give you an idea, but it uses CC2652RB.

    -kel

  • Hello Charlie,

    I would suggest looking at the "att.h" file inside the SDK and look for the "Error Response: Error Code" which should give you a better understanding of the value of "errorCode". You will see several error definitions there.

    For what I see, the error may be related to ATT_ERR_INVALID_HANDLE (0x01) and the fact tha the attribute handle value given doesn't match to any of the servers.

    Here you can also see some information about the ATT API

    BR,

    David.