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.

CC2640: Requests to enable Notify are going to ..._ValueChangeHandler, why?

Part Number: CC2640

I have a project that started out as SimpleLink Academy ProjectZero.

I have added access to external flash and disabled OSAL and uart Logging to have room to run the program on the CC2640 128mb.

I have removed LED and DATA characteristics and added write capabilities to the BUTTON characteristic, which has been renamed to HALO.

When I added write ability to HALO characteristic, I may have done it incorrectly since Notify seems to work only sometimes now and when I have my ios App request a change in Notify to YES, I see the request in my user_HaloService_ValueChangeHandler and the ios request fails with a 'Insufficient Resources' error.

The change notify to YES request shows up as "01:00", or 0x30 0x31 0x3A 0x00 0x00 in the user_HaloService_ValueChangeHandler. 

Why would the request even show up in my user_HaloService_ValueChangeHandler as it should just get intercepted by the BLE 2.2.1 stack, shouldn't it?

Here is my halo_service.c where I might have made a mistake, can anyone see it?

/*********************************************************************
 * INCLUDES
 */
#include <halo_service.h>
#include <string.h>

//#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Diags.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"


/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
* GLOBAL VARIABLES
*/

// Halo_Service Service UUID
CONST uint8_t HaloServiceUUID[ATT_UUID_SIZE] =
{
  HALO_SERVICE_SERV_UUID_BASE128(HALO_SERVICE_SERV_UUID)
};

// OFFSET UUID
CONST uint8_t HS_OFFSETUUID[ATT_UUID_SIZE] =
{
  HS_OFFSET_UUID_BASE128(HS_OFFSET_UUID)
};

// ONOFFREC UUID
CONST uint8_t HS_ONOFFRECUUID[ATT_UUID_SIZE] =
{
  HS_ONOFFREC_UUID_BASE128(HS_ONOFFREC_UUID)
};

// TOTALTIME UUID
CONST uint8_t HS_TOTALTIMEUUID[ATT_UUID_SIZE] =
{
  HS_TOTALTIME_UUID_BASE128(HS_TOTALTIME_UUID)
};

/*********************************************************************
 * LOCAL VARIABLES
 */

static HaloServiceCBs_t *pAppCBs = NULL;
static uint8_t bs_icall_rsp_task_id = INVALID_TASK_ID;

/*********************************************************************
* Profile Attributes - variables
*/

// Service declaration
static CONST gattAttrType_t HaloServiceDecl = { ATT_UUID_SIZE, HaloServiceUUID };

// Characteristic "OFFSET" Properties (for declaration)
static uint8_t HS_OFFSETProps = GATT_PROP_NOTIFY | GATT_PROP_READ | GATT_PROP_WRITE;  //dale added  | GATT_PROP_WRITE  

// Characteristic "OFFSET" Value variable
static uint8_t HS_OFFSETVal[HS_OFFSET_LEN] = {0};

// Length of data in characteristic "OFFSET" Value variable, initialized to minimal size.
static uint16_t HS_OFFSETValLen = HS_OFFSET_LEN_MIN;

// Characteristic "OFFSET" Client Characteristic Configuration Descriptor
static gattCharCfg_t *HS_OFFSETConfig;

// Length of data in characteristic "OFFSET" Value variable, initialized to minimal size. //dale5
static uint16_t hs_OFFSETValLen = HS_OFFSET_LEN_MIN; //dale5




// Characteristic "ONOFFREC" Properties (for declaration)
static uint8_t HS_ONOFFRECProps = GATT_PROP_NOTIFY | GATT_PROP_READ | GATT_PROP_WRITE; //dale added  | GATT_PROP_WRITE 

// Characteristic "ONOFFREC" Value variable
static uint8_t HS_ONOFFRECVal[HS_ONOFFREC_LEN] = {0};

// Length of data in characteristic "ONOFFREC" Value variable, initialized to minimal size.
static uint16_t HS_ONOFFRECValLen = HS_ONOFFREC_LEN_MIN;

// Characteristic "ONOFFREC" Client Characteristic Configuration Descriptor
static gattCharCfg_t *HS_ONOFFRECConfig;

// Length of data in characteristic "ONOFFREC" Value variable, initialized to minimal size. //dale5
static uint16_t hs_ONOFFRECValLen = HS_ONOFFREC_LEN_MIN; //dale5


// Characteristic "TOTALTIME" Properties (for declaration)
static uint8_t HS_TOTALTIMEProps = GATT_PROP_NOTIFY | GATT_PROP_READ | GATT_PROP_WRITE; //dale added  | GATT_PROP_WRITE

// Characteristic "TOTALTIME" Value variable
static uint8_t HS_TOTALTIMEVal[HS_TOTALTIME_LEN] = {0};

// Length of data in characteristic "TOTALTIME" Value variable, initialized to minimal size.
static uint16_t HS_TOTALTIMEValLen = HS_TOTALTIME_LEN_MIN;

// Characteristic "TOTALTIME" Client Characteristic Configuration Descriptor
static gattCharCfg_t *HS_TOTALTIMEConfig;

// Length of data in characteristic "TOTALTIME" Value variable, initialized to minimal size. //dale5
static uint16_t hs_TOTALTIMEValLen = HS_TOTALTIME_LEN_MIN; //dale5

/*********************************************************************
* Profile Attributes - Table
*/

static gattAttribute_t Halo_ServiceAttrTbl[] =
{
    // Halo_Service Service Declaration
    {
        { ATT_BT_UUID_SIZE, primaryServiceUUID },
        GATT_PERMIT_READ,
        0,
        (uint8_t *)&HaloServiceDecl
    },

        // OFFSET Characteristic Declaration
        {
            { ATT_BT_UUID_SIZE, characterUUID },
            GATT_PERMIT_READ,
            0,
            &HS_OFFSETProps
        },
            // OFFSET Characteristic Value
            {
                { ATT_UUID_SIZE, HS_OFFSETUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE, //dale add  | GATT_PERMIT_WRITE
                0,
                HS_OFFSETVal
            },
            // OFFSET CCCD
            {
                { ATT_BT_UUID_SIZE, clientCharCfgUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE,
                0,
                (uint8_t *)&HS_OFFSETConfig
            },

        // ONOFFREC Characteristic Declaration
        {
            { ATT_BT_UUID_SIZE, characterUUID },
            GATT_PERMIT_READ,
            0,
            &HS_ONOFFRECProps
        },
            // ONOFFREC Characteristic Value
            {
                { ATT_UUID_SIZE, HS_ONOFFRECUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE, //dale add  | GATT_PERMIT_WRITE
                0,
                HS_ONOFFRECVal
            },
            // ONOFFREC CCCD
            {
                { ATT_BT_UUID_SIZE, clientCharCfgUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE,
                0,
                (uint8_t *)&HS_ONOFFRECConfig
            },

        // TOTALTIME Characteristic Declaration
        {
            { ATT_BT_UUID_SIZE, characterUUID },
            GATT_PERMIT_READ,
            0,
            &HS_TOTALTIMEProps
        },
            // TOTALTIME Characteristic Value
            {
                { ATT_UUID_SIZE, HS_TOTALTIMEUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE, //dale add  | GATT_PERMIT_WRITE
                0,
                HS_TOTALTIMEVal
            },
            // ONOFFREC CCCD
            {
                { ATT_BT_UUID_SIZE, clientCharCfgUUID },
                GATT_PERMIT_READ | GATT_PERMIT_WRITE,
                0,
                (uint8_t *)&HS_TOTALTIMEConfig
            },

};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t Halo_Service_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 Halo_Service_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 Halo_ServiceCBs =
{
  Halo_Service_ReadAttrCB,  // Read callback function pointer
  Halo_Service_WriteAttrCB, // Write callback function pointer
  NULL                       // Authorization callback function pointer
};

/*********************************************************************
* PUBLIC FUNCTIONS
*/

/*
 * HaloService_AddService- Initializes the HaloService service by registering
 *          GATT attributes with the GATT server.
 *
 *    rspTaskId - The ICall Task Id that should receive responses for Indications.
 */
extern bStatus_t HaloService_AddService( uint8_t rspTaskId )
{
  uint8_t status;

  // Allocate Client Characteristic Configuration table
  HS_OFFSETConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns );
  if ( HS_OFFSETConfig == NULL )
  {
    return ( bleMemAllocError );
  }
  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, HS_OFFSETConfig );




  // Allocate Client Characteristic Configuration table
  HS_ONOFFRECConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns );
  if ( HS_ONOFFRECConfig == NULL )
  {
    return ( bleMemAllocError );
  }
  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, HS_ONOFFRECConfig );




  // Allocate Client Characteristic Configuration table
  HS_TOTALTIMEConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns );
  if ( HS_TOTALTIMEConfig == NULL )
  {
    return ( bleMemAllocError );
  }
  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, HS_TOTALTIMEConfig );




  // Register GATT attribute list and CBs with GATT Server App
  status = GATTServApp_RegisterService( Halo_ServiceAttrTbl,
                                        GATT_NUM_ATTRS( Halo_ServiceAttrTbl ),
                                        GATT_MAX_ENCRYPT_KEY_SIZE,
                                        &Halo_ServiceCBs );
  Log_info1("Registered service, %d attributes", (IArg)GATT_NUM_ATTRS( Halo_ServiceAttrTbl ));
  bs_icall_rsp_task_id = rspTaskId;

  return ( status );
}

/*
 * HaloService_RegisterAppCBs - Registers the application callback function.
 *                    Only call this function once.
 *
 *    appCallbacks - pointer to application callbacks.
 */
bStatus_t HaloService_RegisterAppCBs( HaloServiceCBs_t *appCallbacks )
{
  if ( appCallbacks )
  {
    pAppCBs = appCallbacks;
    Log_info1("Registered callbacks to application. Struct %p", (IArg)appCallbacks);
    return ( SUCCESS );
  }
  else
  {
    Log_warning0("Null pointer given for app callbacks.");
    return ( FAILURE );
  }
}

/*
 * HaloService_SetParameter - Set a HaloService parameter.
 *
 *    param - Profile parameter ID
 *    len   - length of data to write
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t HaloService_SetParameter( uint8_t param, uint16_t len, void *value )
{
  bStatus_t ret = SUCCESS;
  uint8_t  *pAttrVal;
  uint16_t *pValLen;
  uint16_t valMinLen;
  uint16_t valMaxLen;
  uint8_t sendNotiInd = FALSE;
  gattCharCfg_t *attrConfig;
  uint8_t needAuth;

  switch ( param )
  {
    case HS_OFFSET_ID:
      pAttrVal  =  HS_OFFSETVal;
      pValLen   = &HS_OFFSETValLen;
      valMinLen =  HS_OFFSET_LEN_MIN;
      valMaxLen =  HS_OFFSET_LEN;
      sendNotiInd = TRUE;
      attrConfig  = HS_OFFSETConfig;
      needAuth    = FALSE; // Change if authenticated link is required for sending.
      Log_info2("SetParameter : %s len: %d", (IArg)"OFFSET", (IArg)len);
      break;

    case HS_ONOFFREC_ID:
      pAttrVal  =  HS_ONOFFRECVal;
      pValLen   = &HS_ONOFFRECValLen;
      valMinLen =  HS_ONOFFREC_LEN_MIN;
      valMaxLen =  HS_ONOFFREC_LEN;
      sendNotiInd = TRUE;
      attrConfig  = HS_ONOFFRECConfig;
      needAuth    = FALSE; // Change if authenticated link is required for sending.
      Log_info2("SetParameter : %s len: %d", (IArg)"ONOFFREC", (IArg)len);
      break;

    case HS_TOTALTIME_ID:
      pAttrVal  =  HS_TOTALTIMEVal;
      pValLen   = &HS_TOTALTIMEValLen;
      valMinLen =  HS_TOTALTIME_LEN_MIN;
      valMaxLen =  HS_TOTALTIME_LEN;
      sendNotiInd = TRUE;
      attrConfig  = HS_TOTALTIMEConfig;
      needAuth    = FALSE; // Change if authenticated link is required for sending.
      Log_info2("SetParameter : %s len: %d", (IArg)"TOTALTIME", (IArg)len);
      break;

    default:
      Log_error1("SetParameter: Parameter #%d not valid.", (IArg)param);
      return INVALIDPARAMETER;
  }

  // Check bounds, update value and send notification or indication if possible.
  if ( len <= valMaxLen && len >= valMinLen )
  {
    memcpy(pAttrVal, value, len);
    *pValLen = len; // Update length for read and get.

    if (sendNotiInd)
    {
      Log_info2("Trying to send noti/ind: connHandle %x, %s",
                (IArg)attrConfig[0].connHandle,
                (IArg)((attrConfig[0].value==0)?"\x1b[33mNoti/ind disabled\x1b[0m" :
                       (attrConfig[0].value==1)?"Notification enabled" :
                                                "Indication enabled"));
      // Try to send notification.
      GATTServApp_ProcessCharCfg( attrConfig, pAttrVal, needAuth,
                                  Halo_ServiceAttrTbl, GATT_NUM_ATTRS( Halo_ServiceAttrTbl ),
                                  bs_icall_rsp_task_id,  Halo_Service_ReadAttrCB);
    }
  }
  else
  {
    Log_error3("Length outside bounds: Len: %d MinLen: %d MaxLen: %d.", (IArg)len, (IArg)valMinLen, (IArg)valMaxLen);
    ret = bleInvalidRange;
  }

  return ret;
}


/*
 * HaloService_GetParameter - Get a HaloService parameter.
 *
 *    param - Profile parameter ID
 *    len   - pointer to a variable that contains the maximum length that can be written to *value.
              After the call, this value will contain the actual returned length.
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t HaloService_GetParameter( uint8_t param, uint16_t *len, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    default:
      Log_error1("GetParameter: Parameter #%d not valid.", (IArg)param);
      ret = INVALIDPARAMETER;
      break;
  }
  return ret;
}

/*********************************************************************
 * @internal
 * @fn          Halo_Service_findCharParamId
 *
 * @brief       Find the logical param id of an attribute in the service's attr table.
 *
 *              Works only for Characteristic Value attributes and
 *              Client Characteristic Configuration Descriptor attributes.
 *
 * @param       pAttr - pointer to attribute
 *
 * @return      uint8_t paramID (ref Halo_service.h) or 0xFF if not found.
 */
static uint8_t Halo_Service_findCharParamId(gattAttribute_t *pAttr)
{
  // Is this a Client Characteristic Configuration Descriptor?
  if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
    return Halo_Service_findCharParamId(pAttr - 1); // Assume the value attribute precedes CCCD and recurse

  // Is this attribute in "OFFSET"?
  else if ( ATT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, HS_OFFSETUUID, pAttr->type.len))
    return HS_OFFSET_ID;

  // Is this attribute in "ONOFFREC"?
  else if ( ATT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, HS_ONOFFRECUUID, pAttr->type.len))
    return HS_ONOFFREC_ID;

  // Is this attribute in "TOTALTIME"?
  else if ( ATT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, HS_TOTALTIMEUUID, pAttr->type.len))
    return HS_TOTALTIME_ID;

  else
    return 0xFF; // Not found. Return invalid.
}


/*********************************************************************
 * @fn          Halo_Service_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 Halo_Service_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;
  uint16_t valueLen;
  uint8_t paramID = 0xFF;

  // Find settings for the characteristic to be read.
  paramID = Halo_Service_findCharParamId( pAttr );
  switch ( paramID )
  {
    case HS_OFFSET_ID:
      valueLen = HS_OFFSETValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"OFFSET",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for OFFSET can be inserted here */
      break;

    case HS_ONOFFREC_ID:
      valueLen = HS_ONOFFRECValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"ONOFFREC",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for ONOFFREC can be inserted here */
      break;

    case HS_TOTALTIME_ID:
      valueLen = HS_TOTALTIMEValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"TOTALTIME",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for TOTALTIME can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check bounds and return the value
  if ( offset > valueLen )  // Prevent malicious ATT ReadBlob offsets.
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else
  {
    *pLen = MIN(maxLen, valueLen - offset);  // Transmit as much as possible
    memcpy(pValue, pAttr->pValue + offset, *pLen);
  }

  return status;
}




/*********************************************************************
 * @fn      Halo_Service_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 Halo_Service_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;
    uint8_t   changeParamID = 0xFF;
    uint16_t writeLenMin;
    uint16_t writeLenMax;
    uint16_t *pValueLenVar;

    // See if request is regarding a Client Characterisic Configuration //dale5 ??????in or not
    if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
    {
        Log_info3("WriteAttrCB (CCCD): param: %d connHandle: %d %s",
                  (IArg)Halo_Service_findCharParamId(pAttr),
                  (IArg)connHandle,
                  (IArg)(method == GATT_LOCAL_WRITE?"- restoring bonded state":"- OTA write"));

        // Allow notification and indication, but do not check if really allowed per CCCD.
        status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                 offset, GATT_CLIENT_CFG_NOTIFY |
                                                         GATT_CLIENT_CFG_INDICATE );
        if (SUCCESS == status && pAppCBs && pAppCBs->pfnCfgChangeCb)
           pAppCBs->pfnCfgChangeCb( connHandle, HALO_SERVICE_SERV_UUID,
                                    Halo_Service_findCharParamId(pAttr), pValue, len );
        //return status; //dale removed
    }

    //dale5 start : added section for write
    // Find settings for the characteristic to be written.
    paramID = Halo_Service_findCharParamId( pAttr );
    switch ( paramID )
    {
      case HS_OFFSET_ID:
        writeLenMin  = HS_OFFSET_LEN_MIN;
        writeLenMax  = HS_OFFSET_LEN;
        pValueLenVar = &hs_OFFSETValLen;

        Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                   (IArg)"OFFSET",
                   (IArg)connHandle,
                   (IArg)len,
                   (IArg)offset,
                   (IArg)method);
        /* Other considerations for OFFSET can be inserted here */
        break;

      case HS_ONOFFREC_ID:
        writeLenMin  = HS_ONOFFREC_LEN_MIN;
        writeLenMax  = HS_ONOFFREC_LEN;
        pValueLenVar = &hs_ONOFFRECValLen;

        Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                   (IArg)"ONOFFREC",
                   (IArg)connHandle,
                   (IArg)len,
                   (IArg)offset,
                   (IArg)method);
        /* Other considerations for ONOFFREC can be inserted here */
        break;

      case HS_TOTALTIME_ID:
        writeLenMin  = HS_TOTALTIME_LEN_MIN;
        writeLenMax  = HS_TOTALTIME_LEN;
        pValueLenVar = &hs_TOTALTIMEValLen;

        Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                   (IArg)"TOTALTIME",
                   (IArg)connHandle,
                   (IArg)len,
                   (IArg)offset,
                   (IArg)method);
        /* Other considerations for TOTALTIME can be inserted here */
        break;

      default:
        Log_error0("Attribute was not found.");
        return ATT_ERR_ATTR_NOT_FOUND;
    }
    // Check whether the length is within bounds.
    if ( offset >= writeLenMax )
    {
      Log_error0("An invalid offset was requested.");
      status = ATT_ERR_INVALID_OFFSET;
    }
    else if ( offset + len > writeLenMax )
    {
      Log_error0("Invalid value length was received.");
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
    else if ( offset + len < writeLenMin && ( method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ ) )
    {
      // Refuse writes that are lower than minimum.
      // Note: Cannot determine if a Reliable Write (to several chars) is finished, so those will
      //       only be refused if this attribute is the last in the queue (method is execute).
      //       Otherwise, reliable writes are accepted and parsed piecemeal.
      Log_error0("Invalid value length was received.");
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
    else
    {
      // Copy pValue into the variable we point to from the attribute table.
      memcpy(pAttr->pValue + offset, pValue, len);

      // Only notify application and update length if enough data is written.
      //
      // Note: If reliable writes are used (meaning several attributes are written to using ATT PrepareWrite),
      //       the application will get a callback for every write with an offset + len larger than _LEN_MIN.
      // Note: For Long Writes (ATT Prepare + Execute towards only one attribute) only one callback will be issued,
      //       because the write fragments are concatenated before being sent here.
      if ( offset + len >= writeLenMin )
      {
        changeParamID = paramID;
        *pValueLenVar = offset + len; // Update data length.
      }
    }

    // Let the application know something changed (if it did) by using the
    // callback it registered earlier (if it did).
    if (changeParamID != 0xFF)
      if ( pAppCBs && pAppCBs->pfnChangeCb )
        pAppCBs->pfnChangeCb( connHandle, HALO_SERVICE_SERV_UUID, paramID, pValue, len+offset ); // Call app function from stack task context.

    return status;
    //dale end : added section for write
}

And here you can see that a request to set Notify to YES (write opcode 0x12 from master) is responded to by 'insufficient resources' from the slave:

  • SUCCESS, so in Halo_Service_WriteAttrCB, I had removed a 'return status;' when I should not have.

    Without the 'return status;', I think that the notify change confirmation was getting stomped on by the fact that the confirmation was also going out as written data.

    So, putting the 'return status;' back in here seems to fix it but I think an 'else if' may be better:

        if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
        {
            Log_info3("WriteAttrCB (CCCD): param: %d connHandle: %d %s",
                      (IArg)Halo_Service_findCharParamId(pAttr),
                      (IArg)connHandle,
                      (IArg)(method == GATT_LOCAL_WRITE?"- restoring bonded state":"- OTA write"));
    
            // Allow notification and indication, but do not check if really allowed per CCCD.
            status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                     offset, GATT_CLIENT_CFG_NOTIFY |
                                                             GATT_CLIENT_CFG_INDICATE );
            if (SUCCESS == status && pAppCBs && pAppCBs->pfnCfgChangeCb)
               pAppCBs->pfnCfgChangeCb( connHandle, HALO_SERVICE_SERV_UUID,
                                        Halo_Service_findCharParamId(pAttr), pValue, len );
            return status; //dale LEFT THIS IN TO WORK!!!
        }
    

    If there is a better way to combine notify and write there please tell me.

  • Glad you solve the problem!