CSR867x学习笔记:SPP Server and Client

为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板【淘宝链接:思度科技CSR开发板】。

技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–

1. 引言

两个通过SPP协议连接的蓝牙设备之间的数据通信如串口通信一般简单方便,遗憾的是ADK中并没有给出简单的例程,使很多初学者难以快速学会使用SPP协议。

本文给出SPP Server和Client的使能示例代码,并给出运行于matlab的Client端测试方法。

2. SPP简介

SPP是Serial Port Profile(串口配置文件)的缩写,其定义了使用蓝牙进行RS232(或类似)串行电缆仿真的设备应使用的协议和过程。简单来说就是在蓝牙设备之间建立虚拟的串口进行数据通信。

先来看一下SPP协议与其他协议之间的关系图:
CSR867x学习笔记:SPP Server and Client_第1张图片
从上图中可见SPP协议是很多协议的基础协议。蓝牙设备之间通过SDP协议发现彼此的SPP服务。无绳电话和对讲也利用了SPP协议。

接下来看一下协议模型:
CSR867x学习笔记:SPP Server and Client_第2张图片
SPP协议需要SDP协议和RFCOMM协议作为支撑,分别负责服务的发现和数据的传输。在下文的示例代码中会有体现。通常我们将发起SPP连接请求的设备称为Client,接受SPP请求的设备称为Server。建立SPP连接的消息流如下:

Server Client 注册SDP服务和RFCOMM服务通道 查询RFCOMM服务通道 加密验证 验证通过 建立RFCOMM会话 双方交换数据 Server Client

3. VM工程中添加SPP协议

ADK中的SPP相关的代码路径如下:

  • src\lib\spp_common\ spp server和spp client共用的代码
  • src\lib\sppc\ spp client代码
  • src\lib\spps\ spp server代码

VM project properties->build system->Libraries添加spps, sppc, spp_common,源代码中包含spps.h, sppc.h, spp_common.h头文件。

3.1. 启用SPP Server

需要启用SPP Server服务并创建消息处理函数:

/* 启用SPP服务 */
void sink_intercom_init(void)
{
    ITC_DEBUG(("ITC %s\n", __func__));
    
    intercom_task.handler = intercom_handle_message;
    
    SppStartService(&intercom_task);
}

消息处理函数中对SPP Client发起的连接和断开请求作出响应:

/* SPP连接响应 */
case SPP_CONNECT_IND:
{
    SPP_CONNECT_IND_T *m = (SPP_CONNECT_IND_T *)message;

    ITC_DEBUG(("ITC: SPP_CONNECT_IND\n"));

    intercom_server.addr = m->addr;
    intercom_server.server_channel = m->server_channel;
    intercom_server.sink = m->sink;
    
    /* 响应Client设备的请求,payload size = 64 */
    SppConnectResponse(&intercom_task, 
                        &intercom_server.addr,
                        TRUE,
                        intercom_server.sink,
                        intercom_server.server_channel,
                        INTERCOM_SPP_MAX_PAYLOAD_SIZE);
}
break;

/* SPP断开响应 */
case SPP_DISCONNECT_IND:
{
    SPP_DISCONNECT_IND_T *m = (SPP_DISCONNECT_IND_T *)message;
    
    ITC_DEBUG(("ITC: SPP_DISCONNECT_IND\n"));
    
    SppDisconnectResponse(m->spp);
}
break;

在收到SPP Client发来的消息后,原封不动地再发送给Client:

case SPP_MESSAGE_MORE_DATA:
{           
	/* Move data from source into message */
	Source src = ((SPP_MESSAGE_MORE_DATA_T*)message)->source;
	const uint8 *s = SourceMap(src);
	uint16 len = SourceBoundary(src);
	uint8 * spp_buf;
	bool status;
    uint16 claim_result;
    spp_buf = SinkMap(intercom_server.sink);
    
    claim_result = SinkClaim(intercom_server.sink, len);
    if (claim_result == 0xFFFF)
    {
        SourceDrop(src, len);
        return;
    }
    
    memmove (spp_buf+claim_result, s, len);
    status = SinkFlush(intercom_server.sink, len);
    ITC_DEBUG(("ITC: status %d", status));
		
	SourceDrop(src, len);
}
break;

3.2. 启用SPP Client

SPP Client不需要注册SDP服务,只需向指定的蓝牙设备直接发起SPP请求:

SppConnectRequest(&bluedongleTask, &addr, 0, 0); /* 0代表默认参数 */

同时需要准备好处理SPP Client库发送给VM的消息:

/* SPP连接成功,保存sink留作发送数据时用 */
case SPP_CLIENT_CONNECT_CFM:
{
	SPP_CLIENT_CONNECT_CFM_T *m = (SPP_CLIENT_CONNECT_CFM_T *) message;
	/* bool success = m->status == spp_connect_success; */

	spp_sink = m->sink;

	BD_DEBUG(("BD: SPP_CLIENT_CONNECT_CFM: %u, payload size: %d\n", m->status, m->payload_size));
}
break;

/* 断开SPP响应 */
case SPP_DISCONNECT_IND:
{
	SPP_DISCONNECT_IND_T *m = (SPP_DISCONNECT_IND_T *) message;

	spp_sink = NULL;
	
	BD_DEBUG(("BD: SPP_DISCONNECT_IND: %u\n", m->status));
	SppDisconnectResponse(m->spp);
}
break;

处理接收到的SPP Server的数据:

case SPP_MESSAGE_MORE_DATA:
{
	Source src = ((SPP_MESSAGE_MORE_DATA_T*)message)->source;
	const uint8 *s = SourceMap(src);
	uint16 len = SourceBoundary(src);
	
	spp_server_data_process((const char *)s, len);
	SourceDrop(src, len);
}
break;

我们可以在spp_server_data_process里添加程序以处理SPP Server发送来的数据,也可以将数据通过stream机制直接传送给DSP,作为两个蓝牙设备之间DSP数据的透传通道。

4. matlab环境验证SPP收发数据

matlab提供了两种工具可用来验证目标设备的SPP Server是否工作正常。一是虚拟串口,二是蓝牙SPP。

4.1. 虚拟串口

当蓝牙设备与PC机通过蓝牙适配器连接后,如果蓝牙设备支持SPP协议,PC机在扫描到此协议后会虚拟出串口:
CSR867x学习笔记:SPP Server and Client_第3张图片
此时我们需要在matlab中通过访问串口的方式与蓝牙设备建立通信:

spp_uart = serial('COM7');  % 获取串口句柄
fopen(spp_uart);  % 打开串口
fwrite(spp_uart,'111'); % 发送字符串111
recv = fread(spp_uart,9); % 阻塞在此函数直到读取到9个字节
fprintf('%s\n',recv);
fclose(spp_uart); % 关闭串口
delete(spp_uart); % 删除句柄

此方式的优点是连接建立速度较快,缺点是有些设备不能在PC端虚拟出串口(如CSR8670)。

4.2. 蓝牙SPP

matlab支持通过调用蓝牙SPP连接蓝牙设备的SPP Server,此方式支持不能正常枚举出虚拟串口的蓝牙设备,前提是用户已经知道目标设备的SPP server channel并已与其完成配对。示例代码如下:

r=instrhwinfo('Bluetooth');  % 搜索附近的蓝牙设备
d=r.RemoteNames; % 获取这些设备的名称
[m,n] = listdlg('PromptString','Select Your Bluetooth device:','SelectionMode','single','ListString',d); % 列表选择目标蓝牙设备
dn=cell2mat(d(m)); 
dev = Bluetooth(dn, 2) % 通过SPP server channel(此处是2)获取目标蓝牙设备的SPP连接句柄
fopen(dev);  % 建立蓝牙SPP连接
fwrite(dev,'1'); % 发送字符1
fread(dev, 5); % 阻塞等待接收满5字节数据
fclose(dev); % 断开蓝牙SPP连接

5. 总结

在掌握了SPP Server和Client的使能和验证方法后,可以基于SPP协议定制上层协议,开发各种独特应用。这里要注意SPP协议在与安卓设备通信时不受限制,但与iOS设备通信的前提条件是蓝牙设备支持MFi协议。此处可见苹果对任何能够穿透iOS操作系统直达应用的连接技术都是严格管控的,明面上是增加系统稳定性,实质是通过切断客户与终端用户的直接联系,强收过路费。

6. 参考文档

  • Serial Port Profile.pdf

你可能感兴趣的:(蓝牙方案,CSR8670蓝牙芯片软件开发)