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.

CC2650: Notifications appear enabled on phone and can be subscribed to but no notification posted when parameter set.

Part Number: CC2650
Other Parts Discussed in Thread: , CC2640R2F

Hello,

I've been working with the CC2650MODA, but I've altered the board header file so it's compatible with cc2650 launchpad demo codes.

I'm using the BLE sdk 2.2.2.25 example of simple peripheral as the base for my code.

I've been able to create a custom service using the generator here and integrate it into the simple peripheral example  to transmit ADC readings following the guidelines from another tutorial meant for the cc2640r2f with the very critical help of this and this resource. It's been a surprising amount of work actually.

Now I've been fairly successful after a great deal of tinkering I can read adc values, store them in an array and set the first cell of the array as the parameter using SetParameter.

I have then regenerated the custom service also selecting the GATT_PROP_NOTIFY option on the example service generator and updated my code accordingly.

Now, when I go on my app (I'm using LightBlue on android) I am able to subscribe to notifications on my service, but when I "set" a new parameter it isn't being transmitted automatically to my phone. Basically, my phone says I'm subscribed to notifications but it's still only working with manually clicking the "read again" button.

Any thoughts on how to address this issue? Thanks.

  • Michael,

    I've assigned your post internally to someone who can assist. 

  • Hi Evan,

    Thanks very much! Looking forward to their insight.

    Mike

  • Hi Mike,

      Could you please share your custom profile files and I'll review them. Also as you are aware, the simple_peripheral comes with a notifiable characteristic (char 4), so you can confirm/compare that your characteristic is configured the same way as this one.

      Best regards,

        David

  • Hi David,

    Sorry for the slow response,

    Here are my header and c files respectively:

    /**********************************************************************************************
     * Filename:       my2650Therm.h
     *
     * Description:    This file contains the my2650Therm service definitions and
     *                 prototypes.
     *
     * Copyright (c) 2015-2016, 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.
     *
     *************************************************************************************************/
    
    
    #ifndef _MY2650THERM_H_
    #define _MY2650THERM_H_
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    /*********************************************************************
     * INCLUDES
     */
    #include "bcomdef.h"
    /*********************************************************************
     * CONSTANTS
     */
    
    /*********************************************************************
    * CONSTANTS
    */
    // Service UUID
    #define MY2650THERM_SERV_UUID 0xAA00
    
    //  Characteristic defines
    #define MY2650THERM_THERM      0
    #define MY2650THERM_THERM_UUID 0xAA01
    #define MY2650THERM_THERM_LEN  100
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * Profile Callbacks
     */
    
    // Callback when a characteristic value has changed
    typedef void (*my2650ThermChange_t)( uint8 paramID );
    
    typedef struct
    {
      my2650ThermChange_t        pfnChangeCb;  // Called when characteristic value changes
    } my2650ThermCBs_t;
    
    
    
    /*********************************************************************
     * API FUNCTIONS
     */
    
    
    /*
     * My2650Therm_AddService- Initializes the My2650Therm service by registering
     *          GATT attributes with the GATT server.
     *
     */
    extern bStatus_t My2650Therm_AddService( void );
    
    /*
     * My2650Therm_RegisterAppCBs - Registers the application callback function.
     *                    Only call this function once.
     *
     *    appCallbacks - pointer to application callbacks.
     */
    extern bStatus_t My2650Therm_RegisterAppCBs( my2650ThermCBs_t *appCallbacks );
    
    /*
     * My2650Therm_SetParameter - Set a My2650Therm 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).
     */
    extern bStatus_t My2650Therm_SetParameter( uint8 param, uint8 len, void *value );
    
    /*
     * My2650Therm_GetParameter - Get a My2650Therm 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).
     */
    extern bStatus_t My2650Therm_GetParameter( uint8 param, void *value );
    
    /*********************************************************************
    *********************************************************************/
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* _MY2650THERM_H_ */
    
    /**********************************************************************************************
     * Filename:       my2650Therm.c
     *
     * Description:    This file contains the implementation of the service.
     *
     * Copyright (c) 2015-2016, 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 "bcomdef.h"
    #include "OSAL.h"
    #include "linkdb.h"
    #include "att.h"
    #include "gatt.h"
    #include "gatt_uuid.h"
    #include "gattservapp.h"
    #include "gapbondmgr.h"
    
    #include "my2650Therm.h"
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    /*********************************************************************
    * GLOBAL VARIABLES
    */
    
    // my2650Therm Service UUID
    CONST uint8_t my2650ThermUUID[ATT_UUID_SIZE] =
    {
      TI_BASE_UUID_128(MY2650THERM_SERV_UUID)
    };
    
    // therm UUID
    CONST uint8_t my2650Therm_ThermUUID[ATT_UUID_SIZE] =
    {
      TI_BASE_UUID_128(MY2650THERM_THERM_UUID)
    };
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    static my2650ThermCBs_t *pAppCBs = NULL;
    
    /*********************************************************************
    * Profile Attributes - variables
    */
    
    // Service declaration
    static CONST gattAttrType_t my2650ThermDecl = { ATT_UUID_SIZE, my2650ThermUUID };
    
    // Characteristic "Therm" Properties (for declaration)
    static uint8_t my2650Therm_ThermProps = GATT_PROP_READ | GATT_PROP_NOTIFY;
    
    // Characteristic "Therm" Value variable
    static uint8_t my2650Therm_ThermVal[MY2650THERM_THERM_LEN] = {0};
    
    // Characteristic "Therm" CCCD
    static gattCharCfg_t *my2650Therm_ThermConfig;
    
    /*********************************************************************
    * Profile Attributes - Table
    */
    
    static gattAttribute_t my2650ThermAttrTbl[] =
    {
      // my2650Therm Service Declaration
      {
        { ATT_BT_UUID_SIZE, primaryServiceUUID },
        GATT_PERMIT_READ,
        0,
        (uint8_t *)&my2650ThermDecl
      },
        // Therm Characteristic Declaration
        {
          { ATT_BT_UUID_SIZE, characterUUID },
          GATT_PERMIT_READ,
          0,
          &my2650Therm_ThermProps
        },
          // Therm Characteristic Value
          {
            { ATT_UUID_SIZE, my2650Therm_ThermUUID },
            GATT_PERMIT_READ,
            0,
            my2650Therm_ThermVal
          },
          // Therm CCCD
          {
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE,
            0,
            (uint8 *)&my2650Therm_ThermConfig
          },
    };
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    static bStatus_t my2650Therm_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                               uint8 *pValue, uint16 *pLen, uint16 offset,
                                               uint16 maxLen, uint8 method );
    static bStatus_t my2650Therm_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                                uint8 *pValue, uint16 len, uint16 offset,
                                                uint8 method );
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    // Simple Profile Service Callbacks
    CONST gattServiceCBs_t my2650ThermCBs =
    {
      my2650Therm_ReadAttrCB,  // Read callback function pointer
      my2650Therm_WriteAttrCB, // Write callback function pointer
      NULL                       // Authorization callback function pointer
    };
    
    /*********************************************************************
    * PUBLIC FUNCTIONS
    */
    
    /*
     * My2650Therm_AddService- Initializes the My2650Therm service by registering
     *          GATT attributes with the GATT server.
     *
     */
    bStatus_t My2650Therm_AddService( void )
    {
      uint8_t status;
    
      // Allocate Client Characteristic Configuration table
      my2650Therm_ThermConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns );
      if ( my2650Therm_ThermConfig == NULL )
      {
        return ( bleMemAllocError );
      }
    
      // Initialize Client Characteristic Configuration attributes
      GATTServApp_InitCharCfg( INVALID_CONNHANDLE, my2650Therm_ThermConfig );
      // Register GATT attribute list and CBs with GATT Server App
      status = GATTServApp_RegisterService( my2650ThermAttrTbl,
                                            GATT_NUM_ATTRS( my2650ThermAttrTbl ),
                                            GATT_MAX_ENCRYPT_KEY_SIZE,
                                            &my2650ThermCBs );
    
      return ( status );
    }
    
    /*
     * My2650Therm_RegisterAppCBs - Registers the application callback function.
     *                    Only call this function once.
     *
     *    appCallbacks - pointer to application callbacks.
     */
    bStatus_t My2650Therm_RegisterAppCBs( my2650ThermCBs_t *appCallbacks )
    {
      if ( appCallbacks )
      {
        pAppCBs = appCallbacks;
    
        return ( SUCCESS );
      }
      else
      {
        return ( bleAlreadyInRequestedMode );
      }
    }
    
    /*
     * My2650Therm_SetParameter - Set a My2650Therm 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 My2650Therm_SetParameter( uint8 param, uint8 len, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        case MY2650THERM_THERM:
          if ( len == MY2650THERM_THERM_LEN )
          {
            memcpy(my2650Therm_ThermVal, value, len);
    
            // Try to send notification.
            GATTServApp_ProcessCharCfg( my2650Therm_ThermConfig, (uint8_t *)&my2650Therm_ThermVal, FALSE,
                                        my2650ThermAttrTbl, GATT_NUM_ATTRS( my2650ThermAttrTbl ),
                                        INVALID_TASK_ID,  my2650Therm_ReadAttrCB);
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      return ret;
    }
    
    
    /*
     * My2650Therm_GetParameter - Get a My2650Therm 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 My2650Therm_GetParameter( uint8 param, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      return ret;
    }
    
    
    /*********************************************************************
     * @fn          my2650Therm_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 my2650Therm_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                           uint8 *pValue, uint16 *pLen, uint16 offset,
                                           uint16 maxLen, uint8 method )
    {
      bStatus_t status = SUCCESS;
    
      // See if request is regarding the Therm Characteristic Value
    if ( ! memcmp(pAttr->type.uuid, my2650Therm_ThermUUID, pAttr->type.len) )
      {
        if ( offset > MY2650THERM_THERM_LEN )  // Prevent malicious ATT ReadBlob offsets.
        {
          status = ATT_ERR_INVALID_OFFSET;
        }
        else
        {
          *pLen = MIN(maxLen, MY2650THERM_THERM_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      my2650Therm_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 my2650Therm_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                            uint8 *pValue, uint16 len, uint16 offset,
                                            uint8 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);
      }
      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( paramID ); // Call app function from stack task context.
    
      return status;
    }