从机接收主机来的数据并不像网上大部分博客说的那么简单,当然过程是很esay,如果只接收一个字节的数据而且不需要处理的话也很简单,但是涉及到多字节收发的时候,需要注意和处理的地方就很多了。我在调试透传功能时,由于没有合适的调试工具,只能引出串口看当前的数据,所以走了不少弯路,崩溃了近三天之后终于能稳定透传了。
在主机端配置好后,最大的疑惑莫过于当发送的字节数大于1时,主机直接返回的就是Write error 13 。开始以为是主机端有发送字节限制,但看遍代码仍旧没有找到字节限制的define。随后在从机里找错误信息,终于找到居然是从机接收有字节限制!!!定睛一看,simpleBLEperipheral例程给的代码里charac 1只能接收一个字节。OMG~~~~~~
先说write error 13 ,其实是在simpleGATTprofile.c的simpleProfile_WriteAttrCB函数中返回的。
//Validate the value // Make sure it's not a blob oper if ( offset == 0 ) { if ( len != 1 ) { status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; }
Error 13 的定义:#define ATT_ERR_INVALID_VALUE_SIZE 0x0d //!< The attribute value length is invalid for the operation 属性值的长度超了!!!
解决方法:
1 在simpleGATTprofile.c中加入
#define SIMPLEPROFILE_CHAR1_LEN 20
static uint8 simpleProfileChar1[SIMPLEPROFILE_CHAR1_LEN] = {0};
2 在static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] 中改为:
// Characteristic Value 1
{
{ ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
simpleProfileChar1
},
3 替换下面的函数
1)
static bStatus_t simpleProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ) { bStatus_t status = SUCCESS; uint8 notifyApp = 0xFF; // If attribute permissions require authorization to write, return error if ( gattPermitAuthorWrite( pAttr->permissions ) ) { // Insufficient authorization return ( ATT_ERR_INSUFFICIENT_AUTHOR ); } if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { case SIMPLEPROFILE_CHAR1_UUID: case SIMPLEPROFILE_CHAR3_UUID: //Validate the value // Make sure it's not a blob oper /************************* 更改 ********************************/ if ( offset == 0 ) { #if 0 if ( len != SIMPLEPROFILE_CHAR1_LEN ) { status = ATT_ERR_INVALID_VALUE_SIZE; } #else if ( len > SIMPLEPROFILE_CHAR1_LEN ) { status = ATT_ERR_INVALID_VALUE_SIZE; } #endif } else { status = ATT_ERR_ATTR_NOT_LONG; } //Write the value if ( status == SUCCESS ) { uint8 *pCurValue = (uint8 *)pAttr->pValue; osal_memset(pCurValue,0,SIMPLEPROFILE_CHAR1_LEN); VOID osal_memcpy( pCurValue, pValue, len ); if( pAttr->pValue == simpleProfileChar1 ) { notifyApp = SIMPLEPROFILE_CHAR1; } else { notifyApp = SIMPLEPROFILE_CHAR3; } } break; /*************************** 更改结束 ***********************************/ case GATT_CLIENT_CHAR_CFG_UUID: status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY ); break; default: // Should never get here! (characteristics 2 and 4 do not have write permissions) status = ATT_ERR_ATTR_NOT_FOUND; break; } } else { // 128-bit UUID status = ATT_ERR_INVALID_HANDLE; } // If a charactersitic value changed then callback function to notify application of change if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange ) { simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp ); //调用回调函数 simpleProfileChangeCB() } return ( status ); }
2)
2) bStatus_t SimpleProfile_GetParameter( uint8 param, void *value ) { bStatus_t ret = SUCCESS; switch ( param ) { case SIMPLEPROFILE_CHAR1: VOID osal_memcpy( value, simpleProfileChar1, SIMPLEPROFILE_CHAR1_LEN ); //simpleProfileChar1 是数组名 break; case SIMPLEPROFILE_CHAR2: *((uint8*)value) = simpleProfileChar2; break; case SIMPLEPROFILE_CHAR3: *((uint8*)value) = simpleProfileChar3; break; case SIMPLEPROFILE_CHAR4: *((uint8*)value) = simpleProfileChar4; break; case SIMPLEPROFILE_CHAR5: VOID osal_memcpy( value, simpleProfileChar5, SIMPLEPROFILE_CHAR5_LEN ); break; default: ret = INVALIDPARAMETER; break; } return ( ret ); }
通过上面操作,属性配置就算是完成了。这样进行接收的时候就不会出现错误了。
二,从机接收主机数据:
对于从机接收主机发来的数据同样需要在GATT层产生一个GATT ProFile Callback的调用,
// Simple GATT Profile Callbacks
static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs =
{
simpleProfileChangeCB // Charactersitic value change callback
};
它的注册是在// Register callback with SimpleGATTprofile :
VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );
操作设置步骤:
在simpleProfileChangeCB里加入
uint8 newValueBuf[20]={0}; case SIMPLEPROFILE_CHAR1: SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, newValueBuf ); Uart0Send_String (newValueBuf, 20); break; 通过上面的设置即可接收到主机发送来的信息了 ,并且信息储存在了newValueBuf中
三,从机给主机发送数据:
从机给主机发数据: 使用GATT_Notification函数 void sbpSerialAppSendNoti(uint8 *pBuffer,uint16 length) { uint8 len; if(length > 20) len = 20; else len = length; static attHandleValueNoti_t pReport; pReport.handle=0x2E; pReport.len = len; osal_memcpy(pReport.value, pBuffer, len); GATT_Notification( 0, &pReport, FALSE ); }
开始觉得挺麻烦,写写博客其实也挺简单的,唉~~~~~