基于CC2541的主机与基于ESP32模组从机BLE通信调试总结

硬件环境:从机:ESP-32S蓝牙模组

                   主机:基于CC2541蓝牙芯片的USB蓝牙中继模块

需求:主机与从机实现双向通行。从机通过通过通知的方式不断向从机发送数据,主机收到数据是通过USB串口将数据传送到上位机,主机将USB串口的数据通过蓝牙传输给从机,实现对从机的控制。

1、从机端部分代码:

ESP服务UUID

/// Service
static const uint16_t spp_service_uuid = 0xABF0;  //服务
/// Characteristic UUID
#define ESP_GATT_UUID_SPP_DATA_RECEIVE      0xABF1  //接收数据
#define ESP_GATT_UUID_SPP_DATA_NOTIFY       0xABF2   //通知
#define ESP_GATT_UUID_SPP_COMMAND_RECEIVE   0xABF3   //接收命令数据
#define ESP_GATT_UUID_SPP_COMMAND_NOTIFY    0xABF4   //通知命令数据


ESP蓝牙事件响应代码
static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    esp_ble_gatts_cb_param_t *p_data = (esp_ble_gatts_cb_param_t *) param;
    uint8_t res = 0xff;
    uint32_t Len;
    uint16_t i;
   // printf("gatts_profile_event_handler event = %x\n",event);
    switch (event) {
    	case ESP_GATTS_REG_EVT:
    	    ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
        	esp_ble_gap_set_device_name((const char *)g_BLEName); //蓝牙名称

        	ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);

            memcpy(&spp_adv_data[9],g_BLEName,strlen((char*)g_BLEName));
            spp_adv_data[7] = strlen((char*)g_BLEName)+1;
            Len = strlen((char*)g_BLEName)+9;
        	esp_ble_gap_config_adv_data_raw((uint8_t *)spp_adv_data,Len);

        	ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
        	esp_ble_gatts_create_attr_tab(spp_gatt_db, gatts_if, SPP_IDX_NB, SPP_SVC_INST_ID); 
       	break;
    	case ESP_GATTS_READ_EVT:  //客户端读操作
            res = find_char_and_desr_index(p_data->read.handle);
            if(res == SPP_IDX_SPP_STATUS_VAL){
                //TODO:client read the status characteristic
                printf("client read the status characteristic\n");
            }
       	 break;
    	case ESP_GATTS_WRITE_EVT: {  //客户端有写数据
    	    res = find_char_and_desr_index(p_data->write.handle);
            if(p_data->write.is_prep == false){
                ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_WRITE_EVT : handle = %d\n", res);
                if(res == SPP_IDX_SPP_COMMAND_VAL){
                    uint8_t * spp_cmd_buff = NULL;
                    spp_cmd_buff = (uint8_t *)malloc((spp_mtu_size - 3) * sizeof(uint8_t));
                    if(spp_cmd_buff == NULL){
                        ESP_LOGE(GATTS_TABLE_TAG, "%s malloc failed\n", __func__);
                        break;
                    }
                    memset(spp_cmd_buff,0x0,(spp_mtu_size - 3));
                    memcpy(spp_cmd_buff,p_data->write.value,p_data->write.len);
                    printf("00recv Data:%s\n",p_data->write.value);
                    for(i=0;i<p_data->write.len;i++)
                    {
                        ReceiveData(BLE, p_data->write.value[i]);
                    }
                }else if(res == SPP_IDX_SPP_DATA_NTF_CFG){
                    printf("SPP_IDX_SPP_DATA_NTF_CFG:%s\n",p_data->write.value);
                    if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x01)&&(p_data->write.value[1] == 0x00)){
                        enable_data_ntf = true;
                    }else if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x00)&&(p_data->write.value[1] == 0x00)){
                        enable_data_ntf = false;
                    }
                }
                else if(res == SPP_IDX_SPP_DATA_RECV_VAL){
#ifdef SPP_DEBUG_MODE
                    esp_log_buffer_char(GATTS_TABLE_TAG,(char *)(p_data->write.value),p_data->write.len);
#else
                    printf("recv Data:%s\n",p_data->write.value);
                    for(i=0;i<p_data->write.len;i++)
                    {
                        ReceiveData(BLE, p_data->write.value[i]);
                    }
#endif
                }else{
                    //TODO:
                }
            }else if((p_data->write.is_prep == true)&&(res == SPP_IDX_SPP_DATA_RECV_VAL)){
                ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_PREP_WRITE_EVT : handle = %d\n", res);
            }
      	 	break;
    	}
    	case ESP_GATTS_EXEC_WRITE_EVT:{
    	    ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT\n");
    	    break;
    	}
    	case ESP_GATTS_MTU_EVT:
    	    spp_mtu_size = p_data->mtu.mtu;
            printf("Set spp_mtu_size:mtu_size=%d\n",spp_mtu_size);
    	    break;
    	case ESP_GATTS_CONF_EVT:
    	    break;
    	case ESP_GATTS_UNREG_EVT:
        	break;
    	case ESP_GATTS_DELETE_EVT:
        	break;
    	case ESP_GATTS_START_EVT:
        	break;
    	case ESP_GATTS_STOP_EVT:
        	break;
    	case ESP_GATTS_CONNECT_EVT:   //客户端连接事件
    	    spp_conn_id = p_data->connect.conn_id;
    	    spp_gatts_if = gatts_if;
    	    is_connected = true;
    	    memcpy(&spp_remote_bda,&p_data->connect.remote_bda,sizeof(esp_bd_addr_t));
        	break;
    	case ESP_GATTS_DISCONNECT_EVT:
    	    is_connected = false;
    	    enable_data_ntf = false;
    	    esp_ble_gap_start_advertising(&spp_adv_params);
    	    break;
    	case ESP_GATTS_OPEN_EVT:
    	    break;
    	case ESP_GATTS_CANCEL_OPEN_EVT:
    	    break;
    	case ESP_GATTS_CLOSE_EVT:
    	    break;
    	case ESP_GATTS_LISTEN_EVT:
    	    break;
    	case ESP_GATTS_CONGEST_EVT:
    	    break;
    	case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
    	    ESP_LOGI(GATTS_TABLE_TAG, "The number handle =%x\n",param->add_attr_tab.num_handle);
    	    if (param->add_attr_tab.status != ESP_GATT_OK){
    	        ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table failed, error code=0x%x", param->add_attr_tab.status);
    	    }
    	    else if (param->add_attr_tab.num_handle != SPP_IDX_NB){
    	        ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, SPP_IDX_NB);
    	    }
    	    else {
    	        memcpy(spp_handle_table, param->add_attr_tab.handles, sizeof(spp_handle_table));
    	        esp_ble_gatts_start_service(spp_handle_table[SPP_IDX_SVC]);
    	    }
    	    break;
    	}
    	default:
    	    break;
    }
}


2、主机端部分代码


// Simple Profile Service UUID
#define SIMPLEPROFILE_SERV_UUID              0xABF0// 0xFFF0
    
// Key Pressed UUID
#define SIMPLEPROFILE_CHAR1_UUID           0xABF1// 0xFFF1
#define SIMPLEPROFILE_CHAR2_UUID           0xABF2 //0xFFF2
#define SIMPLEPROFILE_CHAR3_UUID           0xABF3 //0xFFF3
#define SIMPLEPROFILE_CHAR4_UUID           0xABF4 //0xFFF4


//开始扫描可以连接的从机
static void simpleBLE_ScanStart( void)
{
    simpleBLEScanning = TRUE;
    simpleBLEScanRes = 0;

    LCD_WRITE_STRING( "Discovering...", HAL_LCD_LINE_1 );
    SerialPrintString("Discovering...\r\n");
    LCD_WRITE_STRING( "", HAL_LCD_LINE_2 );

    GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
                                DEFAULT_DISCOVERY_ACTIVE_SCAN,
                                DEFAULT_DISCOVERY_WHITE_LIST );

}
添加扫描到的设备
static void simpleBLEAddDeviceInfo( uint8 *pAddr, uint8 addrType, uint8 *Name, uint8 NameLen)
{
    uint8 i;
    // If result count not at max
    if ( simpleBLEScanRes < DEFAULT_MAX_SCAN_RES )
    {
        // Check if device is already in scan results
        for ( i = 0; i < simpleBLEScanRes; i++ )
        {
            if ( osal_memcmp( pAddr, simpleBLEDevList[i].addr , B_ADDR_LEN ) )
            {
                return;
            }
        }

        // Add addr to scan result list
        osal_memcpy( simpleBLEDevList[simpleBLEScanRes].addr, pAddr, B_ADDR_LEN );
        osal_memcpy( simpleBLEDevList[simpleBLEScanRes].Name, Name, NameLen);
        simpleBLEDevList[simpleBLEScanRes].addrType = addrType;
        simpleBLEScanRes++;

        Uart_Send_ScanDevInfo(pAddr, Name, NameLen);  //扫描到的设备按照通信协议通过USB串口发出
    }
}

连接设备
static void simpleBLE_Connect( uint8 index)
{
    if ( simpleBLEState == BLE_STATE_IDLE )
    {
       // if there is a scan result
       if ( simpleBLEScanRes > 0 )
       {
           uint8 addrType;
           uint8 *peerAddr;
           // connect to current device in scan result
           peerAddr = simpleBLEDevList[index].addr;
           addrType = simpleBLEDevList[index].addrType;

           simpleBLEState = BLE_STATE_CONNECTING;

           GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE,
                                         DEFAULT_LINK_WHITE_LIST,
                                         addrType, peerAddr );
       }
    }
}
开始发现服务
static void simpleBLECentralStartDiscovery( void )
{
    uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),  //SIMPLEPROFILE_SERV_UUID
                                     HI_UINT16(SIMPLEPROFILE_SERV_UUID)
                                   };

    // Initialize cached handles
    simpleBLESvcStartHdl = simpleBLESvcEndHdl = simpleBLECharHdl = GUA_charHdl[0]= 0;

    simpleBLEDiscState = BLE_DISC_STATE_SVC;

    // Discovery simple BLE service
    GATT_DiscPrimaryServiceByUUID( simpleBLEConnHandle,uuid,ATT_BT_UUID_SIZE,simpleBLETaskId );
  //  GATT_DiscAllPrimaryServices( simpleBLEConnHandle,simpleBLETaskId );
}

发现特指值
static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )
{
    attReadByTypeReq_t req;
    char str[128];
    sprintf(str, "hande =0x%x,pMsg->method = %x,numInfo = %d,pMsg->hdr.status=%x\r\n",BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0], pMsg->msg.readByTypeRsp.dataList[1] ),pMsg->method, pMsg->msg.findByTypeValueRsp.numInfo,pMsg->hdr.status);
    SerialPrintString((uint8 *)str);
    if ( simpleBLEDiscState == BLE_DISC_STATE_SVC )  //发现服务
    {
        // Service found, store handles
        if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->msg.findByTypeValueRsp.numInfo > 0 )
        {
            simpleBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;
            simpleBLESvcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle;
            SerialPrintString("Service discovery\r\n");
        }
        // If procedure complete
        if ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP  &&
                pMsg->hdr.status == bleProcedureComplete ) ||
                ( pMsg->method == ATT_ERROR_RSP ) )
        {
            if ( simpleBLESvcStartHdl != 0 )
            {
                // Discover characteristic
                simpleBLEDiscState = BLE_DISC_STATE_CHAR1;

                req.startHandle = simpleBLESvcStartHdl;
                req.endHandle = simpleBLESvcEndHdl;
                req.type.len = ATT_BT_UUID_SIZE;
                req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
               // GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
                GATT_DiscCharsByUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
                SerialPrintString("Characteristic1 discovery...\r\n");               
            }
        }
    }
    else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR1 )//发现发现特征值1
    {
        if ( pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 )
        {
            simpleBLECharHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0], 
                                           pMsg->msg.readByTypeRsp.dataList[1] );
            sprintf(str, "simpleBLECharHdl= %x\r\n",simpleBLECharHdl);
            SerialPrintString((unsigned char*)str);

            simpleBLEProcedureInProgress = TRUE;//此时仍在进程中
            SerialPrintString("Characteristic1 discovery\r\n");
            //simpleBLEProcedureInProgress = FALSE;
           // simpleBLEDiscState = BLE_DISC_STATE_IDLE;
            osal_start_timerEx( simpleBLETaskId, GUA_READ_CHAR1_EVT, 1000 );//一定要延时一定时间,否则会读取特征值失败。通过这一步才会有后面的Read rsp:0  
        }        
        else
        {
            //注意这里一定要else,当numPairs=0时才能再读下一个,下同
            simpleBLEDiscState = BLE_DISC_STATE_CHAR2;
            req.startHandle = simpleBLESvcStartHdl;
            req.endHandle = simpleBLESvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR2_UUID);
            req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR2_UUID);
            SerialPrintString("Characteristic2 discovery...\r\n");
            //GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
            GATT_DiscCharsByUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
        }
        
    }
    else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR2 )  //发现char2
    {
        //读出char2的handle并保存到GUA_charHdl
        if (pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 )
        {
            GUA_charHdl[1] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0], pMsg->msg.readByTypeRsp.dataList[1] );
            simpleBLEProcedureInProgress = TRUE;  //此时仍在进程中
            sprintf(str, "GUA_charHdl[1]= %x\r\n",GUA_charHdl[1]);
            SerialPrintString((unsigned char*)str);
            SerialPrintString("Characteristic2 discovery\r\n");
        }
        //发送命令读取下一个特征值的句柄
        else
        {
            simpleBLEDiscState = BLE_DISC_STATE_CHAR3;
            req.startHandle = simpleBLESvcStartHdl;
            req.endHandle = simpleBLESvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR3_UUID);
            req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR3_UUID);
            SerialPrintString("Characteristic3 discovery...\r\n");
           // GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
           GATT_DiscCharsByUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
            
        }
        
    } 
    else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR3 ) //发现char3
    {
         sprintf(str, "pMsg->method = %x,numInfo = %d\r\n",pMsg->method,pMsg->msg.readByTypeRsp.numPairs);
        SerialPrintString((uint8 *)str);
        //读出char3的handle并保存到GUA_charHdl
        if ( pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 )
        {
            GUA_charHdl[2] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],pMsg->msg.readByTypeRsp.dataList[1] );
            sprintf(str, "GUA_charHdl[2]= %x\r\n",GUA_charHdl[2]);
            SerialPrintString((unsigned char*)str);
            simpleBLEProcedureInProgress = TRUE;//此时仍在进程中
            SerialPrintString("Characteristic3 discovery\r\n");
        }
        //发送命令读取下一个特征值的句柄 
        else
        { 
            simpleBLEDiscState = BLE_DISC_STATE_CHAR4;
            req.startHandle = simpleBLESvcStartHdl;
            req.endHandle = simpleBLESvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
            req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
            SerialPrintString("Characteristic4 discovery...\r\n");
           // GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
           GATT_DiscCharsByUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
        }
    }
    else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR4 )   //发现char6
    {
        if ( pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 )
        {
            GUA_charHdl[3] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],pMsg->msg.readByTypeRsp.dataList[1] );
            sprintf(str, "GUA_charHdl[3]= %x\r\n",GUA_charHdl[3]);
            SerialPrintString((unsigned char*)str);
            
            SerialPrintString("Characteristic4 discovery\r\n");
            
             //注意最后一个特征值时需要赋值
             simpleBLEProcedureInProgress = FALSE;
        }
        simpleBLEDiscState = BLE_DISC_STATE_IDLE;
        //读完最后的char6,就可以返回闲置模式了
    }
    
}

主机接收从机的通知数据

static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
    switch ( pMsg->event )
    {
    case KEY_CHANGE:
        simpleBLECentral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
        break;

    case GATT_MSG_EVENT:
        simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );
        break;
    }
}

主机向从机写数据(注:写数据当然要判断连接情况等)

uint8 BLE_SendData(uint8 *pBuff, uint8 iLen)
{  
    attWriteReq_t AttReq;
    osal_memset((char*)&AttReq,0,sizeof(attWriteReq_t));
    AttReq.handle = simpleBLECharHdl+1;
    AttReq.len = iLen;
    AttReq.sig = 0;
    AttReq.cmd = 0;
    osal_memcpy(AttReq.value, pBuff, iLen);
    return GATT_WriteCharValue(0, &AttReq,simpleBLETaskId);
}

测试中遇到的问题总结:

在调试过程中,从机给主机发送的通知数据能正常的收到,但是主机无法向从机写数据,总是返回blePending或者bleTimeout错误,

扫描二维码关注公众号,回复: 13570563 查看本文章

经过长时间调试,发现在static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )函数中,按照CC2541开发例程中 使用GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId )特征值,得到的特征值无法写入成功,但是使用  GATT_DiscCharsByUUID( simpleBLEConnHandle, &req, simpleBLETaskId )函数发现特征值,能全部发现从机预设的特征值,并能正确的读写。问题得以解决。由于协议栈底层代码并不开源,未找到两个函数之间的区别。

注:GATT_DiscCharsByUUID读到的句柄,比GATT_ReadUsingCharUUID()读到的句柄小1

猜你喜欢

转载自blog.csdn.net/ggggyj/article/details/87919960