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.

CC2674R10: How to send the full data of a characteristic which contain 256 bytes

Part Number: CC2674R10
Other Parts Discussed in Thread: AFE4960, SYSCONFIG, CC1354P10, , TMP117

Hi team,

I am using the project_zero for my project. I modify the temperature service to send the ECG data from AFE4960 by SPI. In the original program, the temperature is float sent using GATTServApp_ProcessCharCfg as notification, 

The data I want to send over the air is 256 bytes long. With GATTServApp_ProcessCharCfg, this process is done in the task context. I can only send the first 20 bytes of the 256 using notification and I cannot send all the data out. can you give some suggestion to solve this?

Another question is The data from sensor is updated every 128ms,  if found my program the read and send the data in ble is slow, how can i make it fast to read the updated data?

Thanks a lot

  • Hello Matthew,

    Thanks for reaching out.

    Please help me with the following questions.

    1. What SDK version are you using?
    2. What is the Max Size of PDU (bytes) you have configured? See ble.maxPDUSize or General Configurations inside Sysconfig.
    3. Did you enable notifications on the phone? "Enable" Switch on the right.

    I would suggest taking a look at our simple_serial_socket_server example here (which uses notifications and a Max PDU Size of 255).

    BR,

    David.

  • Hi David,

    Thanks for your reply.

    1. The sdk i use is simplelink_cc13xx_cc26xx_sdk_7_10_01_24, the project zero is for CC1354P10_1, I modified it for CC2674R10

    2. The max size i set is 255

    3. Yes i enabled the notification in the phone, and get the notification message of 20bytes like the red box below. i capture the pic when disable the notify.

     for the  simple_serial_socket_server example, this example is for cc26x2, i try to modify it to cc2674, but it not work, i asked the problem in this thread.

    Matthew

  • Hello Matthew,

    Could you please share a bit more about the modification you are doing to the code? (code snippets)

    Have you also increased the size of the characteristic to 255?

    I may be worth checking with a Bluetooth sniffer if the tx payload is actually around 255 to discard an issue on the central device (phone).

    The simple_serial_socket_server example could help you understand the specific modifications to be considered for increased size payload.

    BR,

    David.

  • Hi David,

    please see the code below, the senor is read by  profile_readecgSensor() function and notify by GATTServApp_ProcessCharCfg. For the notification here, how many data can it send over the air? Through the simplelink starter app, i can only receive the data of 20 bytes. Is the limit from app or my program?

    static void Sensors_taskFxn(UArg a0, UArg a1)
    {
      Log_info0("Sensor task started");
    
      // ******************************************************************
      // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages.
       ICall_registerApp(&selfEntity, &syncEvent);
    
      // Initialize queue for application messages.
      // Note: Used to transfer control to application thread from e.g. interrupts.
      Queue_construct(&appMsgQueue, NULL);
      appMsgQueueHandle = Queue_handle(&appMsgQueue);
    
      // Clear service callback list (in case sensors_init was already called)
      List_clearList(&service_cb_map);
    
      // Initialize commonly used drivers
    //  GPIO_init();
      I2C_init();
    //  ADC_init();
    
      // Open shared I2C
      I2C_Params i2cParams;
      I2C_Params_init(&i2cParams);
      i2cParams.bitRate = I2C_400kHz;
      i2cHandle = I2C_open(CONFIG_I2C_0, &i2cParams);
      if (i2cHandle == NULL)
      {
        Log_error0("I2C handle failed to open");
        //TMP117Profile_init(&i2cHandle);
      }
      else
      {
        Log_info0("I2C handle opened");
    
        TMP117Profile_init(&i2cHandle); ////////Replaced by TMP117
    
      }
    
    
    
      // Indicate to application task that initialization has completed
      Semaphore_post(Semaphore_handle(&semTaskReady));
    
      // Task main loop
      for(;;)
      {
          uint32_t events;
    
          // Waits for an event to be posted associated with the calling thread.
          // Note that an event associated with a thread is posted when a
          // message is queued to the message receive queue of the thread
          events = Event_pend(syncEvent, Event_Id_NONE, SC_ALL_EVENTS,
                              ICALL_TIMEOUT_FOREVER);
    
          if(events)
          {
              ICall_EntityID dest;
              ICall_ServiceEnum src;
              ICall_HciExtEvt *pMsg = NULL;
    
              // Fetch any available messages that might have been sent from the stack
              if(ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
              {
                // We don't care about these but free in case we receive any
                ICall_freeMsg(pMsg);
              }
    
              // Process messages sent from another task or another context.
              while(!Queue_empty(appMsgQueueHandle))
              {
                  scMsg_t *pMsg = (scMsg_t *)Util_dequeueMsg(appMsgQueueHandle);
                  if(pMsg)
                  {
                      if (pMsg->event == SC_DEFERRED_EVT)
                      {
                        Sensors_performDeferred((def_params_t *)(pMsg->pData));
                      }
                      Read ECG data by SPI
                      if (pMsg->event == ECG_READ_EVT)
                      {
                          profile_readecgSensor(ECG_SEND_Flag);
                      }
    
                      // Free the received message.
                      ICall_free(pMsg);
                  }
              }
          }
      }
    } 
     
     
     *  ======== TMP117Profile_init ========
     */
    bStatus_t TMP117Profile_init(I2C_Handle *pHandle)
    {
      bStatus_t status = SUCCESS;
      if ((pHandle == NULL) || (*pHandle == NULL))
      {
        return INVALIDPARAMETER;
      }
    
      // Store pointer to i2c handle
      pI2cHandle = pHandle;
    
      // Initialize params
      OPT3001_Params_init(&opt3001Params);
      // Enable sensor
      status  = TMP117Profile_enable();
    //  status  = 0;
    
      // Register with common sensors module
      if (status == SUCCESS)
      {
        status = Sensors_registerCbs(TMP117_SERV_UUID, processCBs);
      }
    
      if (status == SUCCESS)
      {
        // Add service
        status = TMP117_AddService();
      }
    
      if (status == SUCCESS)
      {
        // Register callbacks with service
        status = TMP117_RegisterProfileCBs(&sensorServiceCBs);
      }
    
      spiffs_file fd;
      spiffs_config fsConfig;
      //uint8_t errFlag=0;
    
      Display_init();
      display = Display_open(Display_Type_UART, NULL);
      uint8_t         transmitBuffer[MSGSIZE]={0x00,0x00,0x00,0x08};
      uint8_t         receiveBuffer[MSGSIZE];
      bool            transferOK;
    
      GPIO_setConfig(CONFIG_ADC_RDY, GPIO_CFG_IN_PD | GPIO_CFG_IN_INT_RISING);
      GPIO_setCallback(CONFIG_ADC_RDY, ADCRDYFxn0);
    
      GPIO_write(CONFIG_AFE_RST, 0);
      usleep(50);
      GPIO_write(CONFIG_AFE_RST, 1);
      GPIO_write(CONFIG_GPIO_CS, 1);
      //for(uint8_t i=0;i<8;i++)
      //{
       //   transmitBuffer[i]=i;
     // }
      SPI_init();  // Initialize the SPI driver defer
      SPI_Params_init(&spiParams);  // Initialize SPI parameters
      spiParams.frameFormat = SPI_POL0_PHA0;
      /* See device-specific technical reference manual for supported speeds */
      spiParams.bitRate     = 10000000;
      spiParams.dataSize = 8;       // 8-bit data size
      Log_info0("Starting the AFE4960 example\n---------------------------------------------------");
    
      GPIO_enableInt(CONFIG_ADC_RDY);
    
    #ifdef test_ecg
        /*software reset AFE4960*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x00,0x08,&spiParams,&spiTransaction);
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x01,0x00,&spiParams,&spiTransaction);
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x00,0x02,&spiParams,&spiTransaction);
    
        /*measurement mode config*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x0a,0x84004a,&spiParams,&spiTransaction);
        /*clock tree config */
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xbf,0x012682,&spiParams,&spiTransaction);
        /*clock config ,see table 7-9*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xc0,0x120e20,&spiParams,&spiTransaction);
        /*Bio-Z excitation config fEXC_BIOZ=1Hz */
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xcc,0x001f40,&spiParams,&spiTransaction);
        /*DAC output swing config ±256mV */
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xcd,0x000402,&spiParams,&spiTransaction);
        /*Bio-z output at ECG1 and ECG2*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xcb,0x000065,&spiParams,&spiTransaction);
        /*128kHz don't output at GPIO2*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x4b,0x000100,&spiParams,&spiTransaction);
        /*enablt interrupt output at GPIO2*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x29,0x080000,&spiParams,&spiTransaction);
        /*test signal input at ECG1 and ECG2*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xc8,0x010184,&spiParams,&spiTransaction);
        /*count RAC=96*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xc1,0x000080,&spiParams,&spiTransaction);
        /*REG_NUM_TS=1 and TS0, TS1 */
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xc4,0x000001,&spiParams,&spiTransaction);
        /*TS0=E, TS1=E*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0xc2,0x000009,&spiParams,&spiTransaction);
        /*FIFO_RDY interrupt on ADC_RDY pin*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x42,0x081fe0,&spiParams,&spiTransaction);
        /*FIFO_RDY interrupt on ADC_RDY pin*/
        //errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x02,0x000001,&spiParams,&spiTransaction);
        /*selct page 0*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x01,0x000000,&spiParams,&spiTransaction);
        /*clr count*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x00,0x000002,&spiParams,&spiTransaction);
        /*enable fifo */
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x00,0x000040,&spiParams,&spiTransaction);
        /*enable timer*/
        errFlag=biosensor_SPI_config(CONFIG_SPI_0,0x1d,0xC00000,&spiParams,&spiTransaction);
    #endif
        Log_info0("finish configuration---------------------------------------------------");
    
    
        // Create one-shot clock for periodic sensor reading event swi
        if (status == SUCCESS)
        {
        uint8_t period = 0;
        TMP117_GetParameter(TMP117_PERIOD_ID, &period);
        Util_constructClock(&clkPeriodic, Sensors_clockHandler,
                            period * SENSOR_PERIOD_RESOLUTION, 0,
                            false, (UArg)NULL);    // Read the AFE4960 adc data by spi
    //    GPIO_enableInt(CONFIG_ADC_RDY);
        profile_initialized = TRUE;
        Log_info0("TMP117 Profile Initialized");
        }
      else
      {
        Log_error1("TMP117 Profile failed to initialize with status 0x%x", status);
      }
    
      return status;
    }
    
    void Sensors_clockHandler(UArg deferred_target)
    {
    //   // Call application to switch context
       Sensors_defer((deferredTarget_t)deferred_target, NULL);
    
    
    }
    
    bStatus_t Sensors_defer(deferredTarget_t pFn, uint8_t *pParams)
    {
        bStatus_t status = SUCCESS;
    
        // Allocate container for deferred function and params
        def_params_t *pData = ICall_malloc(sizeof(def_params_t));
    
        if (pData == NULL)
        {
          status = bleMemAllocError;
        }
        else
        {
            pData->pFn = pFn;
            pData->pParams = pParams;
    
            if(Sensors_enqueueMsg(ECG_READ_EVT, pData) != SUCCESS)
            {
              if (pData != NULL)
              {
                ICall_free(pData);
              }
              status = bleMemAllocError;
            }
        }
    
        return(status);
    }
    bStatus_t profile_readecgSensor(uint8_t a)
    {
        profile_read_msg_t profile_read_msg;
    
        profile_read_msg.event = PROFILE_FILE_READ_EVT;
    
        if (dataRDY!=0) // When data ready, dataRDY = 1
        {
            dataRDY=0;
            GPIO_disableInt(CONFIG_ADC_RDY); // disable the interrupt
            errFlag=biosensor_SPIFIFO_readout(CONFIG_SPI_0,0xff,385,&spiParams,&spiTransaction);// read the adc data and store in ADCdatareceiveBuffer
            for(char i=0;i<128;i++)                                                                  // transfer the data to float *128
            {
                ADCdataconvert[i]=(uint32_t)((ADCdatareceiveBuffer[3*i+1]&(0xff))<<16)|
                                                (uint32_t)((ADCdatareceiveBuffer[3*i+2]&(0xff))<<8)|
                                                    (uint32_t)((ADCdatareceiveBuffer[3*i+3]&(0xff)));
                mid = (uint32_t)(ADCdataconvert[i]&0x800000);
    
                // mid=ADCdataconvert[i];
                if(mid!=0x800000)
                {
                    adcdata[i]=(float)(ADCdataconvert[i]*0.0004768)*(float)1.1;
    
                }
                else
                {
                    adcdata[i]=(float)((16777215-ADCdataconvert[i])*0.0004768)*(float)(-1.1);
    
                }
    
            }
    
    
                // Set profile value (and notify if notifications are enabled)
                TMP117_SetParameter(TMP117_DATA_ID, 256, &adcdata[0]);            // send the 256 byte data BY BLE
    
            Util_startClock(&clkPeriodic);
            GPIO_enableInt(CONFIG_ADC_RDY);
        }
        return SUCCESS;
    }
    
    bStatus_t TMP117_SetParameter( uint8_t param, uint16_t len, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
      //--------------------------------------------------------------------------------------------------------//DATA1
        case TMP117_DATA_ID:
          if ( len == TMP117_DATA_LEN )
          {
            memcpy(TMP117_DataVal, value, len);
    
            // Try to send notification.
            GATTServApp_ProcessCharCfg( TMP117_DataConfig, (uint8_t *)&TMP117_DataVal, FALSE,
                                        TMP117AttrTbl, GATT_NUM_ATTRS( TMP117AttrTbl ),
                                        INVALID_TASK_ID,  TMP117_ReadAttrCB);
    
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
          
          }
          }

    Matthew

  • Hello Matthew,

    Please allow me a day to go through the code.

    There shouldn't be a limit coming from the phone, however a ble sniffer is always good to make sure the peripheral payload size is actually what is expected.

    I am guessing the TMP117_DataVal is defined in a different file? simple_gatt_profile.c maybe? Have you also increased the size of the characteristic?

    BR,

    David.

  • Hi David,

    the TMP117_DataVal is defined as below and the length is set to be 256. The notification received by the phone is 20bytes

    // Characteristic "Data" Properties (for declaration)
    static uint8_t TMP117_DataProps = GATT_PROP_READ | GATT_PROP_NOTIFY;
    // Characteristic "Data" Value variable
    static uint8_t TMP117_DataVal[TMP117_DATA_LEN] = {0};
    
    
    //  Characteristic DATA 1 defines
    #define TMP117_DATA_ID   3
    #define TMP117_DATA_UUID 0xAA01
    #define TMP117_DATA_LEN  256

    Matthew

  • Hi Matthew,

    Looking through your code, I was not able to see where you were allocating the Notification. The code should look something like the SimpleStreamServerI-transmitNode() function (link to our GitHub). The function is shown below:

    /*********************************************************************
     * @fn          SimpleStreamServer_transmitNode
     *
     * @brief       Transmits as much as possible of a SimpleStreamNode_t node
     *              over BLE notifications.
     *
     * @param       node - The node to send
     *
     * @return      SUCCESS, FAILURE, INVALIDPARAMETER, MSG_BUFFER_NOT_AVAIL,
     *              bleNotCOnnected, bleMemAllocError, blePending, bleInvaludMtuSize or
     *              bleTimeout
     */
    static bStatus_t SimpleStreamServer_transmitNode( SimpleStreamNode_t* node)
    {
        bStatus_t ret = SUCCESS;
        attHandleValueNoti_t noti;
        linkDBInfo_t connInfo;
    
        // Find out what the maximum MTU size is
        ret = linkDB_GetInfo(node->connHandle, &connInfo);
    
        // Queue up as many notification slots as possible
        if ( (ret == SUCCESS) && (node != NULL) ) {
    
            // Determine allocation size
            uint16_t allocLen = (node->len - node->offset);
            if ( allocLen > (connInfo.MTU - SSS_NOTI_HDR_SIZE) )
            {
                allocLen = connInfo.MTU - SSS_NOTI_HDR_SIZE;
            }
    
            noti.len = 0;
            noti.pValue = (uint8 *)GATT_bm_alloc( node->connHandle, ATT_HANDLE_VALUE_NOTI,
                                                  allocLen, &noti.len );
    
            // If allocation was successful, copy out data out of the buffer and send it
            if (noti.pValue) {
    
                memcpy(noti.pValue, (void *) ((uint8_t *) node->payload + node->offset), noti.len);
    
                // The outgoing data attribute offset is 4
                noti.handle = SimpleStreamServerAttrTbl[4].handle;
    
                ret = GATT_Notification( node->connHandle, &noti, FALSE );
    
                // If unable to send the message, free allocated buffers and return
                if ( ret != SUCCESS )
                {
                    GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );
                }
                else
                {
                    // Increment node data offset
                    node->offset += noti.len;
                }
            }
            else
            {
                // Unable to allocate space for a notification, return failure
                ret = bleMemAllocError;
            }
        }
    
        return ret;
    }

    As you can see, there is a call to GATT_bm_alloc() to allocation the actual notification first. I am not seeing that function in your code snippets. It should be located somewhere as we are sending a notification over the air. I would like us to confirm what length we are allocating for each notification. Have the function where we call GATT_Notification() will be helpful as well.

    Taking a bluetooth sniffer log will allow us to determine if we are sending a payload with the expected length and the phone is not processing it correctly or if we are sending an incorrect amount and the issue is on the peripheral device.

    Best Regards,

    Jan

  • Hi Jan,

    i cannot find the function GATT_bm_alloc()  in the icall_api_lite.c file, but this block in my project is marked as not used, no other calling on this function i can find in the project. the function i call notification is GATTServApp_ProcessCharCfg. it is unavailable to see how this function to process the notification.

    #ifndef STACK_LIBRARY
    /* This variable exit on teh stack side, and it is always equal to the maximum
     number of possible connection. It is also used in a file share between
    application and stack. */
    uint8 linkDBNumConns = MAX_NUM_BLE_CONNS;
    
    /*********************************************************************
     * @fn      GATT_bm_alloc
     *
     * @brief   GATT implementation of the allocator functionality.
     *
     *          Note: This function should only be called by GATT and
     *                the upper layer protocol/application.
     *
     * @param   connHandle - connection that message is to be sent on.
     * @param   opcode - opcode of message that buffer to be allocated for.
     * @param   size - number of bytes to allocate from the heap.
     * @param   pSizeAlloc - number of bytes allocated for the caller from the heap.
     *
     * @return  pointer to the heap allocation; NULL if error or failure.
     */
    void *GATT_bm_alloc(uint16 connHandle, uint8 opcode, uint16 size, uint16 *pSizeAlloc)
    {
      if (pfnBMAlloc != NULL)
      {
        return (*pfnBMAlloc)(BM_MSG_GATT, size, connHandle, opcode, pSizeAlloc);
      }
    
      return ((void *)NULL);
    }
    
    /*********************************************************************
     * @fn      GATT_bm_free
     *
     * @brief   GATT implementation of the de-allocator functionality.
     *
     * @param   pMsg - pointer to the message containing the memory to free.
     * @param   opcode - opcode of the message.
     *
     * @return  none
     */
    void GATT_bm_free(gattMsg_t *pMsg, uint8 opcode)
    {
      if (pfnBMFree != NULL)
      {
        (*pfnBMFree)(BM_MSG_GATT, pMsg, opcode);
      }
    }
    
    /*********************************************************************
     * @fn      L2CAP_bm_alloc
     *
     * @brief   L2CAP implementation of the allocator functionality.
     *
     *          Note: This function should only be called by L2CAP and
     *                the upper layer protocol/application.
     *
     * @param   size - number of bytes to allocate from the heap.
     *
     * @return  pointer to the heap allocation; NULL if error or failure.
     */
    void *L2CAP_bm_alloc( uint16 size )
    {
      if (pfnBMAlloc != NULL)
      {
        return (*pfnBMAlloc)(BM_MSG_L2CAP, size, 0, 0, NULL);
      }
    
      return ((void *)NULL);
    }
    #endif /* !STACK_LIBRARY */
    

    Matthew

  • Hi Matthew,

    The provided function is used to dynamically allocate the notification payload before sending it with GATT_Notification().

    Best Regards,

    Jan