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.

streaming BLE data with CC2650

Expert 1340 points
Other Parts Discussed in Thread: CC2650, ADS1298, ADS1291

I have the TI ADS1298ECG-FE hooked up to a CC2650DK and am having trouble streaming data over BLE to an Android 2013 Nexus 7 using 7.5ms connection intervals. MAX_NUM_PDU = 6 or 10, and MAX_PDU_SIZE = 23. The ADS1298ECG-FE is configured for 3 active channels @ 250Sps, which asserts DRDYb every 4ms. Every DRYb, the data is stored into a global buffer. 

For testing, I buffered different numbers of samples (1S (9B) every 4ms; 2S (18B) every 8ms; 4S (36B) every 16ms) before calling HeartRate_enqueueMsg(). As the data streams, GATT_Notification() fails intermittently and I lose packets. I don't understand why since the data is ready every 4 or 8ms and fits into one PDU or 16ms and fits into two PDUs (I split it manually and call GATT_Notification() twice in this case). Also even if I just send one packet every 16ms, 24ms or even longer, I still see GATT_Notification() fail intermittently. My code is:

static void HeartRate_serviceCB(uint8_t event)
{
  // Enqueue the message.
  HeartRate_enqueueMsg(HEARTRATE_MEAS_EVT, event);
}


static void HeartRate_measNotify(void)
{
	...
	  heartRateMeas.pValue = GATT_bm_alloc(gapConnHandle, ATT_HANDLE_VALUE_NOTI, HEARTRATE_MEAS_LEN, NULL);
	  if (heartRateMeas.pValue != NULL)
	  {
		...
		// Send notification.
		if (HeartRate_MeasNotify(gapConnHandle, &heartRateMeas) != SUCCESS)
		{
			PINCC26XX_setOutputValue(Board_LED3, 1);
		  GATT_bm_free((gattMsg_t *)&heartRateMeas, ATT_HANDLE_VALUE_NOTI);
		}	
	  }

}

bStatus_t HeartRate_MeasNotify(uint16 connHandle, attHandleValueNoti_t *pNoti)
{
  uint16 value = GATTServApp_ReadCharCfg(connHandle, 
                                         heartRateMeasClientCharCfg);

  // If notifications enabled
  if (value & GATT_CLIENT_CFG_NOTIFY)
  {
    // Set the handle.
    pNoti->handle = heartRateAttrTbl[HEARTRATE_MEAS_VALUE_POS].handle;
  
    // Send the notification.
    return GATT_Notification(connHandle, pNoti, FALSE);
  }

  return bleIncorrectMode;
}

I don't understand why GATT_Notification() should fail if I'm calling it at a relatively slow rate and there are enough PDUs. Also, if I try to loop and resend data when GATT_Notification() fails, then I seem to hang the firmware. How can I fix these issues?

  • Hi, what is the return error value after you send GATT_Notification()? You can also look the UART to BLE reference design to see how the GATT_Notification are implemented to stream data in the SPPBLEServer project:
    www.ti.com/.../TIDC-SPPBLE-SW-RD
  • I got status = 0x16. What does this mean? Thanks!
  • It's defined in bcomdef.h:
    #define blePending 0x16 //!< Waiting

    Can you try increasing MAX_NUM_PDU? Also how are you setting up your connection interval?
  • I tried 20 and it does the same. I set the connection interval to 7.5ms in GAPRole_SetParameter().

    I see comdef.h in simplelink\ble_cc26xx_2_01_00_44423\Components\osal\include but I don't see bcomdef.h. comdef.h doesn't define Waiting.

  • I want to make sure your are really using the correct connection interval. Just for testing, can you set these parameters in the heartRate.c file:

    // Whether to enable automatic parameter update request when a connection is
    // formed.
    #define DEFAULT_ENABLE_UPDATE_REQUEST                   TRUE

    // Minimum connection interval (units of 1.25ms) if automatic parameter update
    // request is enabled.
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL               8

    // Maximum connection interval (units of 1.25ms) if automatic parameter update
    // request is enabled.
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL               8

    Another way to verify is to use a sniffer to capture the over the air packets.

  • 7.5ms connection interval is confirmed with the Frontline BPA LE and configured similarly to what you posted (using 6 for min). BTW thanks for the SPP example; it looks really good :) I'll study it and see if I can get hints.

    I'm basing my firmware off the HeartRate example. I'm trying to different approaches:

    1) When I get a DRYb interrupt, I read the data in the interrupt handler. Then I post an event and the application calls GATT_Notification() when the event in processed. However, if I use the CC2650 SPI peripheral (in blocking mode) this does not work at all. The long SPI read seems to hang the firmware. So I implemented a bit bang SPI and this works, but I get the intermittent GATT_Notification() failure.

    2) When I get a DRYb interrupt, I post an event and the application reads the data using the SPI peripheral (no need for bit bang SPI) and then calls GATT_Notification() when the event is processed. I still get the intermittent GATT_Notification() failure. 

    I'm wondering if the SPI read is too long and that I need to use the sensor controller. But before I try that, what else can I do to make this work? Non-blocking SPI might be difficult for me to implement since I need to worry about callbacks on each ADS1298 register configuration.

    Thanks!

  • Actually SPI is not the issue; I skipped the SPI reads upon DRBYb assertion and simply send dummy data. But still GATT_Notification() fails intermittently.
  • Actually something strange is happening just with the basic HeartRate firmware. I modified it slightly to blast data as follows:

    static void HeartRate_taskFxn(UArg a0, UArg a1)
    {
      // Initialize the application.
      HeartRate_init();
      
      // Application main loop.
      for(;;)
      {
    ...    
        // Heart rate service periodic task.
        if (gHRNotifyEnabled && events & HEARTRATE_MEAS_PERIODIC_EVT)
        {
          events &= ~HEARTRATE_MEAS_PERIODIC_EVT;
          
          HeartRate_measPerTask();
        }
        
    ...
      }
    }
    
    static void HeartRate_measPerTask(void)
    {
      if (gapProfileState == GAPROLE_CONNECTED)
      {
        // Send heart rate measurement notification.
        HeartRate_measNotify();
        
        // Restart timer.
    //    Util_startClock(&measPerClock);
        events |= HEARTRATE_MEAS_PERIODIC_EVT;
        Semaphore_post(sem);
      }
    }
    
    static void HeartRate_heartRateEvt(uint8_t event)
    {
      if (event == HEARTRATE_MEAS_NOTI_ENABLED)
      {
        // If connected start periodic measurement.
        if (gapProfileState == GAPROLE_CONNECTED)
        {
    //      Util_startClock(&measPerClock);
            gHRNotifyEnabled = 1;
            events |= HEARTRATE_MEAS_PERIODIC_EVT;
            Semaphore_post(sem);
        }
      }
      else if (event == HEARTRATE_MEAS_NOTI_DISABLED)
      {
        // Stop periodic measurement.
    //    Util_stopClock(&measPerClock);
          gHRNotifyEnabled = 0;
      }
    ...
    }
    
    static void HeartRate_measNotify(void)
    {	
      attHandleValueNoti_t heartRateMeas;
      PINCC26XX_setOutputValue(Board_LED4, 1);
      heartRateMeas.pValue = GATT_bm_alloc(gapConnHandle, ATT_HANDLE_VALUE_NOTI,
                                           HEARTRATE_MEAS_LEN, NULL);
      if (heartRateMeas.pValue != NULL)
      {
    	heartRateMeas.len = HEARTRATE_MEAS_LEN;
        uint8_t *buf = heartRateMeas.pValue;
        ...
        // Send notification.
        if (HeartRate_MeasNotify(gapConnHandle, &heartRateMeas) != SUCCESS)
        {
        	PINCC26XX_setOutputValue(Board_LED2, 1);
        	PINCC26XX_setOutputValue(Board_LED3, 1);
          GATT_bm_free((gattMsg_t *)&heartRateMeas, ATT_HANDLE_VALUE_NOTI);
        }
        else
        {
        	PINCC26XX_setOutputValue(Board_LED3, 0);	
        }
      }
      PINCC26XX_setOutputValue(Board_LED4, 0);
    }

    The result is I get 85kbps stream most of the time to an Android Nexus 7 2013 with 7.5ms connection intervals. However you can see numerous data dropouts, which is what I'm seeing with the interrupt+event driven version of the firmware. Below is a Frontline capture showing this.

    This occurs even if I use the measPerClock timer to slow down the semaphore posts. What is happening to cause these data dropouts? I can see HeartRate_MeasNotify() fails even if I slow down the semaphore posts (I tried 40ms and shorter). Thanks for your help!

  • Hello,

    Can you post your sniffer file (.cfa only!). Also, have you increased the HEAPMGR_SIZE? You previously mentioned that MAX_PDU_SIZE is 23, however the default & min value is 27. Please keep this at 27.

    Note that as packets are re-transmitted or not ack'd by the Central device, this will cause new notifications to back up and you will eventually get the blePending return code unless you throttle the GATT Notification calls. Since you are using Notifications, you won't know this unless you implement a feedback mechanism using GATT Writes from the Client, for example.

    Best wishes
  • HI JXS,

    MAX_PDU_SIZE is 27; I didn't change it.

    I think you're onto something about the central not ack'ing. I tried a 2013 Asus Nexus 7 w/ Android 5.1 both at fresh power up and after reconnecting. It has a lot of these drop outs, especially after a reconnect to the peripheral. But  a 2013 Nexus 7 w/ Android 4.4 doesn't show this behavior. You can see it does almost drop out on the "after power up" capture, but it's not too bad.

    I also see some weird behavior in scanning in Android 5.1 where my Android app scans for 1sec then stops and rescans after 100ms continuously. (I did this so that I can update RSSI since it doesn't seem to update automatically like iOS does.)  But on Android 5.1 I don't always detect the advertisers on each scan, whereas I detect them most of the time with Android 4.4.

    Any thoughts on stabilizing this firmware/Android app so it's robust on any Android version?

    Thanks!

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/538/Capture_2D00_2015_2D00_09_2D00_03_5F00_171138-.7z

  • Hi,

    Looking at the Capture-2015-09-03_171138 bad - android5.1 after reconnect.cfa, the slave is setting the MD=1, indicating that it has more data to send, but the master (phone) is electing not to continue the connection event. Your slave implementation looks good though.

    I'm not sure about Android as the stack behavior is internal to the respective phone manufacturer, but others have reported discovery issues with Android 5.1 when TxAdd is 0. Perhaps try upgrading to 5.1.1?

    Best wishes
  • Hi Tosa,

    did you got it to work?

    I am using the CC2650 with ADS1291 and want to do the same. I had some trouble with the SPI on Blocking Mode and i change it to callback mode and it worked.

    I am also using the Heartrate Project and i can send the values from it with the preprogrammed measnotification.

    But i will speed it up as you did and send 125 samples per second from it.

    What should i be aware of?

    regards,

    Michael

  • Hello Tosa,
    Greetings from India.
    Hey I am trying to send stored log data from my CC2650 to my android phone.

    Did your application finally work?

    Can you please help me? I am trying to send my stored log data from cc2650 flash to my android phone.

    The app I am using BLE Scanner.

    I am toggling the LED on cc2650 through BLE Scanner and storing the data of the phone which last toggled the LED.

    The data consists of the the mobile phone number, data time stamp and led state.

    I want to download the log on my android phone.

    I am failing to achieve this last step. Can you help me? Can you tell me how you are trying to read data on android as well?
  • Hi Keyshav,

    the heartrate project example is a good way to start. It sends notifications to your device if your phone connects to the heartrate service.
    If your app doesnt connect, try sensortag app it will. But the sensortag app on the heartrate service, will show only 8bit values.

    If you get this to work then implement the heartrate service or do your own service into your project, which will send notifications when data is ready for example.

    If you send time stemps you will have to break it into 8Bit values and save it into an array for the notification.
    On the other side you will have to build the time stemp correctly back.

    It wont work if your andoird app doesnt connect to the service and doesnt rebuild the time stemp back as you wish.

    Michael
  • Dear Michael,
    Thank You for the reply. Where can I find the heart rate project and what all hardware will i require apart from CC2650. Also which android app are you using? Is there an apk file which you can share? It would be of much help. I am not an android expert so need an apk file to test run the code.

    Regards,
    Keyshav
  • Dear Michael,
    I failed to remind you that I am not working on sensortag . I am working on cc2650 launchpad. I don't have the heart rate sensor or any other sensor on board.

    Thank You.

    Regards,
    Keyshav
  • Hi Keyshav,

    please open a new question on the forum so ti can track it too.
    If you have the latest Code composer studio 6.2 for example, you can install simplelink from installung ble stack 2.2 www.ti.com/.../ble-stack
    There you have examples project for every board including Launchpad CC2650 and the heartrate project is also there available.
    For the heartrate project you dont need to have any heart rate sensor, because the project simulates heart rate pulses.

    I dont use android but you can install TI SensorTag App thatb is already available for android and ios.
    If you want to create your own app you could get the source from android from here: www.ti.com/.../sensortag-sw

    Michael
  • Error connecting to the target:
    (Error -600 @ 0x0)
    A required dynamic library could not be located.
    The library isn't on the search path.
    (Emulation package 6.0.222.0)

    Dear Michael, 

                               There is no error while building the project. However, there are errors while debugging the project. Any comments?

    Regards,

    Keyshav

  • Hi Keyshav,

    please open a new question so TI Employee can track it too.