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:
