BR/EDR SPP
The Serial Port Profile defines the protocols and procedures that shall be used by devices using Bluetooth for RS232 (or similar) serial cable emulation. The scenario covered by this profile deals with legacy applications using Bluetooth as a cable replacement, through a virtual serial port abstraction (which in itself is operating system-dependent). The purpose of this document is to give an overview of the Serial Port Profile and demo application. The SPP demo project implements a simple SPP application and can be used as a framework for developing SPP applications.
Overview
SPP Role Features
SPP roles are typically defined as follows.
Device A - This is the device that takes initiative to form a connection to another device.
SPP client
Device B - This is the device that waits for another device to take initiative to connect.
SPP server
SPP Profile Stack
Protocols and entities used in SPP profile are shown below.
The Baseband LMP and L2CAP are the OSI layer 1 and 2 Bluetooth protocols.
RFCOMM is the Bluetooth adaptation of GSM TS 07.10 , providing a transport protocol for serial port emulation.
SDP is the Bluetooth Service Discovery Protocol.
SPP UUID
Universally Unique Identifier(UUID) is a universally unique identifier that is guaranteed to be unique across all space and all time. UUIDs can be independently created in a distributed fashion. No central registry of assigned UUIDs is required. A UUID is a 128-bit value.
The full 128-bit value of a 16-bit or 32-bit UUID may be computed by a simple arithmetic operation.
128_bit_value = 16_bit_value * 296 + Bluetooth_Base_UUID
128_bit_value = 32_bit_value * 296 + Bluetooth_Base_UUID
The Base UUID(Bluetooth_Base_UUID) is used for calculating 128-bit UUIDs from ‘short UUIDs’ (16-bit UUID and 32-bit UUID) as described in the SDP Specification 00000000-0000-1000-8000-00805F9B34FB.
A 16-bit UUID may be converted to 32-bit UUID format by zero-extending the 16-bit value to 32-bits. An equivalent method is to add the 16-bit UUID value toa zero-valued 32-bit UUID.
Blutetooth SDP UUID numbers are defined in https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/.
SPP UUID in SPP server and SPP client should be the same and registered using bt_spp_service_register.
Different SPP applications shall use different SPP UUIDs.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
Build Target |
---|---|---|
RTL87x3E HDK |
RTL87x3E EVB |
bt_spp_demo_4M_bank0 bt_spp_demo_16M_bank0 |
RTL87x3D HDK |
RTL87x3D EVB |
bt_spp_demo_16M_bank0 |
This sample project can be found under board\evb\bt_spp_demo
in SDK folder structure. Users can choose the project according to the Board Name
and choose the Build Target
according to the flash map.
When built for an xxx_4M_xxx
build target, the sample is configured to build and run with a 4M flash map.
When built for an xxx_8M_xxx
build target, the sample is configured to build and run with an 8M flash map.
When built for an xxx_16M_xxx
build target, the sample is configured to build and run with a 16M flash map.
The sample also requires another EVB board. SPP connection can be established and data can be transferred between two EVB boards.
To quickly set up the development environment, please refer to the detailed instructions provided in Quick Start.
Configurations
-
UART receiver/transmitter PINMUX is configured by the macro definition
SPP_DEMO_UART_RX
andSPP_DEMO_UART_TX
in the filesdk\src\sample\bt_spp_demo\app_bt_spp_demo_main.c
. SPP Role
SPP role is configured by the macro definition
SPP_DEMO_SERVER_ROLE
in the filesdk\src\sample\bt_spp_demo\app_flag.h
. One EVB board should configure the macro as 0 which is the client. Another EVB board should configure the macro as 1 which is the server.
Building and Downloading
Take the project rtl87x3e_spp_demo.uvprojx
and target bt_spp_demo_4M_bank0
as an example, to build and run the sample with Keil development environment, follow the steps listed below:
Open
rtl87x3e_spp_demo.uvprojx
.Choose the build target
bt_spp_demo_4M_bank0
.Configure the macro definition
SPP_DEMO_SERVER_ROLE
.Build the target:
After a successful build, the APP bin filebt_spp_demo_bank0_MP-v0.0.0.0-xxx.bin
will be generated in the directorybin\rtl87x3e\flash_4M_dualbank\bank0
.Download APP bin into EVB board.
Configure the macro definition
SPP_DEMO_SERVER_ROLE
as another role, build and download APP bin into another EVB board.Press reset button in the two EVB boards.
Experimental Verification
In order to demo SPP application, user needs to do step by step as follows:
Prepare 2 sets of Evolution Boards, EVB 1 and EVB 2.
Build and Download the SPP demo application binary to the EVBs.
Generate SPP server demo bin and download to EVB 1:
Set theSPP_DEMO_SERVER_ROLE
inapp_flag.h
to 1, build and generatespp_demo_bank0_MP-xxx.bin
, download it to EVB 1.Generate SPP client demo bin and download to EVB 2:
Set theSPP_DEMO_SERVER_ROLE
inapp_flag.h
to 0, build and generatespp_demo_bank0_MP-xxx.bin
, download it to EVB 2.
Reset and power on two EVBs.
Input command
spp connect
to connect SPP between EVB 1 and EVB 2. The console will printSPP Connected:
informing user that SPP is connected.UART Command to Input
Output of UART Response
Description
spp connect 0x11 0x22 0x55 0x77 0x88 0x99
SPP Connected: addr 0x112255667788, local_server_chann 0x03
Connect SPP with remote demo EVB.
After UART prints
SPP Connected:
, data TRX can be tested through SPP.Input command
spp send_data
to send SPP data. The console will printSPP send data to addr
informing user that SPP is sent. When the remote device received data, the console will printSPP receive data from addr
.UART Command to Input
Output of UART Response
Description
spp send_data 0x11 0x22 0x55 0x77 0x88 0x99 0x12
SPP receive data from addr 0x112255667788, local_server_chann 0x03
Send SPP data to remote demo EVB.
Code Overview
This section describes some parts of the source codes used in application of this project.
Source Code Directory
Project directory:
sdk\board\evb\bt_spp_demo
.Project source code directory:
sdk\src\sample\bt_spp_demo
.
Source files in the sample project are currently categorized into several groups as below.
└── Project: bt_spp_demo_16M_bank0
├── include ROM UUID header files. Users do not need to modify it.
├── lib Includes all binary symbol files that user application is built on.
├── cmsis The cmsis source code. Users do not need to modify it.
└── app The application source code.
├── app_bt_spp_demo_app.c
├── app_bt_spp_demo_gap.c
├── app_bt_spp_demo_link.c
├── app_bt_spp_demo_main.c
├── app_bt_spp_demo_sdp.c
├── app_console_msg.c
├── app_io_msg.c
├── app_bt_spp_demo_console.c
├── console_uart.c
├── app_dlps.c
Initialization
Main function is invoked when the application is powered on or the chip resets and performs the following initialization functions.
int main(void)
{
board_init();
driver_init();
task_init();
framework_init();
app_bt_gap_init();
spp_demo_gap_init();
spp_demo_sdp_init();
spp_demo_app_init();
app_spp_demo_cmd_register();
os_sched_start();
return 0;
}
SPP profile could be initialized as follows.
SPP server/client should support the same SPP service at the same time by registering related UUID with bt_spp_service_register.
SPP server/client may process the corresponding event by registering SPP profile callback handler with bt_mgr_cback_register.
void spp_demo_app_init(void)
{
spp_demo_init();
bt_spp_init();
bt_spp_service_register((uint8_t *)spp_demo_service_class_uuid128, SPP_DEMO_RFC_SPP_CHANN_NUM);
bt_mgr_cback_register(spp_demo_app_bt_cback);
bt_spp_ertm_mode_set(false);
}
SPP SDP Record
SPP SDP local record should be registered by bt_sdp_record_add, SPP SDP local record includes the SPP service num. Moreover, SPP service num may be different in different SPP application.
For SPP, 16-bit UUID 0x1101 is reserved, meanwhile, for vendor Serial Port, a 128-bit vendor UUID may be defined and used by the user.
SPP server may support SPP service, vendor SPP service or more services at the same time by registering the related SDP record.
const uint8_t spp_demo_sdp_record[] =
{
SDP_DATA_ELEM_SEQ_HDR,
0x4C,
//attribute SDP_ATTR_SRV_CLASS_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
(uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_SERIAL_PORT >> 8),
(uint8_t)(UUID_SERIAL_PORT),
//attribute SDP_ATTR_PROTO_DESC_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
(uint8_t)SDP_ATTR_PROTO_DESC_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x0c,
SDP_DATA_ELEM_SEQ_HDR,
03,
SDP_UUID16_HDR,
(uint8_t)(UUID_L2CAP >> 8),
(uint8_t)UUID_L2CAP,
SDP_DATA_ELEM_SEQ_HDR,
0x05,
SDP_UUID16_HDR,
(uint8_t)(UUID_RFCOMM >> 8),
(uint8_t)UUID_RFCOMM,
SDP_UNSIGNED_ONE_BYTE,
SPP_DEMO_RFC_SPP_CHANN_NUM,
//attribute SDP_ATTR_BROWSE_GROUP_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
(uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
(uint8_t)UUID_PUBLIC_BROWSE_GROUP,
//attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
(uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x09,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_LANG_ENGLISH >> 8),
(uint8_t)SDP_LANG_ENGLISH,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_CHARACTER_UTF8 >> 8),
(uint8_t)SDP_CHARACTER_UTF8,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
(uint8_t)SDP_BASE_LANG_OFFSET,
//attribute SDP_ATTR_PROFILE_DESC_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
(uint8_t)SDP_ATTR_PROFILE_DESC_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x08,
SDP_DATA_ELEM_SEQ_HDR,
0x06,
SDP_UUID16_HDR,
(uint8_t)(UUID_SERIAL_PORT >> 8),
(uint8_t)UUID_SERIAL_PORT,
SDP_UNSIGNED_TWO_BYTE,
0x01,//version 1.2
0x02,
//attribute SDP_ATTR_SRV_NAME
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
(uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
SDP_STRING_HDR,
0x0B,
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x72, 0x74 //"serial port"
};
void spp_demo_sdp_init(void)
{
bt_sdp_record_add((void *)spp_demo_sdp_record);
}
GAP Callback Handler
The callback function below is used to handle Bluetooth GAP related events.
The callback function will call bt_spp_registered_uuid_check to check if remote device support SPP profile by UUID registered or not and get local SPP service number when received SDP information.
For SPP, 16-bit UUID 0x1101 is reserved, meanwhile, for vendor Serial Port, 128-bit vendor UUID may be defined and used by the user.
SPP may support SPP service, vendor SPP service or more services at the same time by registering the related UUID.
static const uint8_t spp_demo_service_class_uuid128[16] =
{
0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
};
SPP Callback Handler
SPP link credit means the SPP TX transport window size and SPP frame size means the SPP TRX transport MTU size.
If SPP link credit is zero, then, it will be set according to remote device credit. Anyway, if SPP link credits are not zero, APP could send the data.
static void spp_demo_app_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
......
switch (event_type)
{
case BT_EVENT_SPP_CONN_CMPL:
{
p_link = spp_demo_find_link(param->spp_conn_cmpl.bd_addr);
if (p_link != NULL)
{
service = spp_demo_find_service(param->spp_conn_cmpl.bd_addr,
param->spp_conn_cmpl.local_server_chann);
if (service == NULL)
{
service = spp_demo_alloc_service(param->spp_conn_cmpl.bd_addr,
param->spp_conn_cmpl.local_server_chann);
if (service != NULL)
{
char console_buf[80];
service->credit = param->spp_conn_cmpl.link_credit;
service->frame_size = param->spp_conn_cmpl.frame_size;
sprintf((char *)console_buf, "SPP Connected: addr 0x%x%x%x%x%x%x, local_server_chann 0x%x \r\n",
param->spp_conn_cmpl.bd_addr[0],
param->spp_conn_cmpl.bd_addr[1],
param->spp_conn_cmpl.bd_addr[2],
param->spp_conn_cmpl.bd_addr[3],
param->spp_conn_cmpl.bd_addr[4],
param->spp_conn_cmpl.bd_addr[5],
param->spp_conn_cmpl.local_server_chann);
console_write((uint8_t *)console_buf, strlen(console_buf));
}
}
bt_device_mode_set(BT_DEVICE_MODE_DISCOVERABLE_CONNECTABLE);
}
}
break;
case BT_EVENT_SPP_CREDIT_RCVD:
{
service = spp_demo_find_service(param->spp_credit_rcvd.bd_addr,
param->spp_credit_rcvd.local_server_chann);
if (service != NULL)
{
service->credit = param->spp_credit_rcvd.link_credit;
}
}
break;
case BT_EVENT_SPP_DATA_IND:
{
service = spp_demo_find_service(param->spp_data_ind.bd_addr,
param->spp_data_ind.local_server_chann);
if (service != NULL)
{
char console_buf[90];
bt_spp_credits_give(param->spp_data_ind.bd_addr, param->spp_data_ind.local_server_chann, 1);
sprintf((char *)console_buf, "SPP receive data from addr 0x%x%x%x%x%x%x, local_server_chann 0x%x \r\n",
param->spp_data_ind.bd_addr[0],
param->spp_data_ind.bd_addr[1],
param->spp_data_ind.bd_addr[2],
param->spp_data_ind.bd_addr[3],
param->spp_data_ind.bd_addr[4],
param->spp_data_ind.bd_addr[5],
param->spp_data_ind.local_server_chann);
console_write((uint8_t *)console_buf, strlen(console_buf));
console_write(param->spp_data_ind.data, param->spp_data_ind.len);
}
}
break;
case BT_EVENT_SPP_CONN_IND:
{
p_link = spp_demo_find_link(param->spp_conn_ind.bd_addr);
if (p_link != NULL)
{
service = spp_demo_find_service(param->spp_conn_ind.bd_addr,
param->spp_conn_ind.local_server_chann);
if (service == NULL)
{
bt_spp_connect_cfm(p_link->bd_addr,
param->spp_conn_ind.local_server_chann,
true,
param->spp_conn_ind.frame_size,
SPP_DEMO_SPP_DEFAULT_CREDITS);
}
else
{
bt_spp_connect_cfm(p_link->bd_addr,
param->spp_conn_ind.local_server_chann,
false,
param->spp_conn_ind.frame_size,
SPP_DEMO_SPP_DEFAULT_CREDITS);
}
}
}
break;
......
User Command
This section provides a detailed description of the usage of user commands for the SPP demo.
The SPP demo requires inputting commands through the Data UART in the host terminal to perform interaction. This allows various actions to be executed and the application to be controlled.
SPP Connect
The flow of the spp connect
command is to connect SPP with a remote device.
User can input command below to connect SPP. The address in the command is the remote Bluetooth device.
spp connect [remote_device_bluetooth_address]
The console will print SPP Connected:
informing user that SPP is connected.
Information |
Description |
---|---|
Command |
spp connect |
Parameters |
Remote Bluetooth address |
Usage |
Connect SPP with remote device. |
Example |
spp connect 11 22 33 44 55 66 |
SPP Send Data
The flow of the spp send_data
command is to send data through SPP to a remote device. User can input command below to send data. The address in the command is the remote Bluetooth device.
spp send_data [remote_device_bluetooth_address] [data]
The console will print SPP send data to addr
informing user that SPP has been sent. When the remote device receives data, the console will print SPP receive data from addr
.
Information |
Description |
---|---|
Command |
spp send_data |
Parameters |
Remote Bluetooth address and data |
Usage |
Send SPP data to remote device. |
Example |
spp send_data 11 22 33 44 55 66 99 |