mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
Merge branch 'nimble_spp' of ssh://gitlab.espressif.cn:27227/espressif/esp-idf into nimble_spp
Nimble: Added BLE SPP Service Closes: https://github.com/espressif/esp-idf/issues/7303 See merge request espressif/esp-idf!14998
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(spp_server)
|
117
examples/bluetooth/nimble/ble_spp/spp_server/README.md
Normal file
117
examples/bluetooth/nimble/ble_spp/spp_server/README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
||||
|
||||
# BLE SPP peripheral example
|
||||
|
||||
In Bluetooth classic (BR/EDR) systems, a Serial Port Profile (SPP) is an adopted profile defined by the Bluetooth Special Interest Group (SIG) used to emulate a serial port connection over a Bluetooth wireless connection. For BLE systems, an adopted SPP profile over BLE is not defined, thus emulation of a serial port must be implemented as a vendor-specific custom profile.
|
||||
|
||||
This reference design consists of two Demos, the BLE SPP server and BLE SPP client that run on their respective endpoints. These devices connect and exchange data wirelessly with each other. This capability creates a virtual serial link over the air. Each byte input can be sent and received by both the server and client. The SPP server is implemented as the [spp_server](../spp_server) demo while the SPP client is implemented as the [spp_client](../spp_client) demo. Espressif designed the BLE SPP applications to use the UART transport layer but you could adapt this design to work with other serial protocols, such as SPI.
|
||||
|
||||
This vendor-specific custom profile is implemented in [main.c](../spp_client/main/main.c) and [main.c](../spp_server/main/main.c).
|
||||
|
||||
## Using Examples
|
||||
|
||||
### Initialization
|
||||
|
||||
Both the server and client will first initialize the UART and BLE. The server demo will set up the serial port service with standard GATT and GAP services in the attribute server. The client demo will scan the BLE broadcast over the air to find the SPP server.
|
||||
|
||||
### Event Processing
|
||||
|
||||
The SPP server has two main event processing functions for BLE event:
|
||||
|
||||
```c
|
||||
static int ble_spp_server_gap_event(struct ble_gap_event *event, void *arg);
|
||||
static int ble_svc_gatt_handler(uint16_t conn_handle, uint16_t attr_handle,struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
```
|
||||
|
||||
The SPP client has one main event processing functions for BLE event:
|
||||
|
||||
```c
|
||||
esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param);
|
||||
```
|
||||
|
||||
These are some queues and tasks used by SPP application:
|
||||
|
||||
Queues:
|
||||
|
||||
* spp_uart_queue - Uart data messages received from the Uart
|
||||
|
||||
Tasks:
|
||||
|
||||
* `ble_server_uart_task` - process Uart
|
||||
|
||||
### Packet Structure
|
||||
|
||||
After the Uart received data, the data will be posted to Uart task. Then, in the UART_DATA event, the raw data may be retrieved. The max length is 120 bytes every time.
|
||||
If you run the BLE SPP demo with two ESP32 chips, the MTU size will be exchanged for 200 bytes after the ble connection is established, so every packet can be send directly.
|
||||
If you only run the ble_spp_server demo, and it was connected by a phone, the MTU size may be less than 123 bytes. In such a case the data will be split into fragments and send in turn.
|
||||
In every packet, we add 4 bytes to indicate that this is a fragment packet. The first two bytes contain "##" if this is a fragment packet, the third byte is the total number of the packets, the fourth byte is the current number of this packet.
|
||||
The phone APP need to check the structure of the packet if it want to communicate with the ble_spp_server demo.
|
||||
|
||||
### Sending Data Wirelessly
|
||||
|
||||
The client will be sending WriteNoRsp packets to the server. The server side sends data through notifications. When the Uart receives data, the Uart task places it in the buffer.
|
||||
|
||||
### Receiving Data Wirelessly
|
||||
|
||||
The server will receive this data in the BLE_GATT_ACCESS_OP_WRITE_CHR event.
|
||||
|
||||
### GATT Server Attribute Table
|
||||
|
||||
charactertistic|UUID|Permissions
|
||||
:-:|:-:|:-:
|
||||
SPP_DATA_RECV_CHAR|0xABF1|READ&WRITE_NR
|
||||
SPP_DATA_NOTIFY_CHAR|0xABF2|READ&NOTIFY
|
||||
SPP_COMMAND_CHAR|0xABF3|READ&WRITE_NR
|
||||
SPP_STATUS_CHAR|0xABF4|READ & NOTIFY
|
||||
|
||||
This example creates GATT client and performs passive scan, it then connects to peripheral device if the device advertises connectability and the write characteristic.
|
||||
|
||||
It performs three GATT operations against the specified peer:
|
||||
|
||||
* Discover all services,characteristics and descriptors.
|
||||
|
||||
* After the discovery is completed, take UART input from user and write characteristic.
|
||||
|
||||
|
||||
Note :
|
||||
|
||||
* Make sure to run `python -m pip install --user -r $IDF_PATH/requirements.txt -r $IDF_PATH/tools/ble/requirements.txt` to install the dependency packages needed.
|
||||
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
|
||||
|
||||
## How to use example
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
This is the console output on successful connection:
|
||||
|
||||
```
|
||||
I (464) NimBLE_SPP_BLE_PRPH: BLE Host Task Started
|
||||
GAP procedure initiated: stop advertising.
|
||||
Device Address: 7c:df:a1:40:3e:fa
|
||||
GAP procedure initiated: advertise; disc_mode=2 adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=0 adv_itvl_max=0
|
||||
connection established; status=0 handle=1 our_ota_addr_type=0 our_ota_addr=7c:df:a1:40:3e:fa our_id_addr_type=0 our_id_addr=7c:df:a1:40:3e:fa peer_ota_addr_type=0 peer_ota_addr=7c:df:a1:c2:19:92 peer_id_addr_type=0 peer_id_addr=7c:df:a1:c2:19:92 conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0
|
||||
|
||||
I (6924) NimBLE_SPP_BLE_PRPH: Data received in write event,conn_handle = 1,attr_handle = 11
|
||||
1b5b41I
|
||||
(10824) NimBLE_SPP_BLE_PRPH: Notification sent successfully
|
||||
|
||||
```
|
@@ -0,0 +1,6 @@
|
||||
set(srcs "main.c"
|
||||
"gatt_svr.c"
|
||||
"misc.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "$ENV{IDF_PATH}/components")
|
@@ -0,0 +1,7 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_IO_TYPE
|
||||
int "IO Type"
|
||||
default 3
|
||||
|
||||
endmenu
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef H_BLESPPSERVER_
|
||||
#define H_BLESPPSERVER_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nimble/ble.h"
|
||||
#include "modlog/modlog.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ble_hs_cfg;
|
||||
struct ble_gatt_register_ctxt;
|
||||
|
||||
/** GATT server. */
|
||||
#define GATT_SVR_SVC_ALERT_UUID 0x1811
|
||||
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
|
||||
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
|
||||
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
|
||||
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
|
||||
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
|
||||
|
||||
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||
int new_gatt_svr_init(void);
|
||||
|
||||
/* Console */
|
||||
int scli_init(void);
|
||||
int scli_receive_key(int *key);
|
||||
|
||||
/** Misc. */
|
||||
void print_bytes(const uint8_t *bytes, int len);
|
||||
void print_addr(const void *addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
207
examples/bluetooth/nimble/ble_spp/spp_server/main/gatt_svr.c
Normal file
207
examples/bluetooth/nimble/ble_spp/spp_server/main/gatt_svr.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_uuid.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include "ble_spp_server.h"
|
||||
|
||||
/* 16 Bit Alert Notification Service UUID */
|
||||
#define BLE_SVC_ANS_UUID16 0x1811
|
||||
|
||||
/* 16 Bit Alert Notification Service Characteristic UUIDs */
|
||||
#define BLE_SVC_ANS_CHR_UUID16_SUP_NEW_ALERT_CAT 0x2a47
|
||||
#define BLE_SVC_ANS_CHR_UUID16_NEW_ALERT 0x2a46
|
||||
#define BLE_SVC_ANS_CHR_UUID16_SUP_UNR_ALERT_CAT 0x2a48
|
||||
#define BLE_SVC_ANS_CHR_UUID16_UNR_ALERT_STAT 0x2a45
|
||||
#define BLE_SVC_ANS_CHR_UUID16_ALERT_NOT_CTRL_PT 0x2a44
|
||||
|
||||
/**
|
||||
* The vendor specific security test service consists of two characteristics:
|
||||
* o random-number-generator: generates a random 32-bit number each time
|
||||
* it is read. This characteristic can only be read over an encrypted
|
||||
* connection.
|
||||
* o static-value: a single-byte characteristic that can always be read,
|
||||
* but can only be written over an encrypted connection.
|
||||
*/
|
||||
|
||||
/* 59462f12-9543-9999-12c8-58b459a2712d */
|
||||
static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
|
||||
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
|
||||
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
|
||||
|
||||
/* 5c3a659e-897e-45e1-b016-007107c96df6 */
|
||||
static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
|
||||
BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
|
||||
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
|
||||
|
||||
/* 5c3a659e-897e-45e1-b016-007107c96df7 */
|
||||
static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
|
||||
BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
|
||||
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
|
||||
|
||||
static uint8_t gatt_svr_sec_test_static_val;
|
||||
|
||||
static int
|
||||
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt,
|
||||
void *arg);
|
||||
|
||||
struct ble_gatt_svc_def gatt_svr_svcs[] = {
|
||||
{
|
||||
/*** Service: Security test. */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = &gatt_svr_svc_sec_test_uuid.u,
|
||||
.characteristics = (struct ble_gatt_chr_def[])
|
||||
{ {
|
||||
/*** Characteristic: Random number generator. */
|
||||
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
|
||||
.access_cb = gatt_svr_chr_access_sec_test,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
|
||||
}, {
|
||||
/*** Characteristic: Static value. */
|
||||
.uuid = &gatt_svr_chr_sec_test_static_uuid.u,
|
||||
.access_cb = gatt_svr_chr_access_sec_test,
|
||||
.flags = BLE_GATT_CHR_F_READ |
|
||||
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
|
||||
}, {
|
||||
0, /* No more characteristics in this service. */
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
|
||||
void *dst, uint16_t *len)
|
||||
{
|
||||
uint16_t om_len;
|
||||
int rc;
|
||||
|
||||
om_len = OS_MBUF_PKTLEN(om);
|
||||
if (om_len < min_len || om_len > max_len) {
|
||||
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||
}
|
||||
|
||||
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
|
||||
if (rc != 0) {
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt,
|
||||
void *arg)
|
||||
{
|
||||
const ble_uuid_t *uuid;
|
||||
int rand_num;
|
||||
int rc;
|
||||
|
||||
uuid = ctxt->chr->uuid;
|
||||
|
||||
/* Determine which characteristic is being accessed by examining its
|
||||
* 128-bit UUID.
|
||||
*/
|
||||
|
||||
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
|
||||
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
|
||||
|
||||
/* Respond with a 32-bit random number. */
|
||||
rand_num = rand();
|
||||
rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
|
||||
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) {
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||
rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
|
||||
sizeof gatt_svr_sec_test_static_val);
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||
rc = gatt_svr_chr_write(ctxt->om,
|
||||
sizeof gatt_svr_sec_test_static_val,
|
||||
sizeof gatt_svr_sec_test_static_val,
|
||||
&gatt_svr_sec_test_static_val, NULL);
|
||||
return rc;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unknown characteristic; the nimble stack should not have called this
|
||||
* function.
|
||||
*/
|
||||
assert(0);
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
void
|
||||
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char buf[BLE_UUID_STR_LEN];
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_REGISTER_OP_SVC:
|
||||
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||
ctxt->svc.handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_CHR:
|
||||
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
|
||||
"def_handle=%d val_handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||
ctxt->chr.def_handle,
|
||||
ctxt->chr.val_handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_DSC:
|
||||
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
|
||||
ctxt->dsc.handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
new_gatt_svr_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ble_svc_gap_init();
|
||||
ble_svc_gatt_init();
|
||||
|
||||
rc = ble_gatts_count_cfg(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ble_gatts_add_svcs(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
443
examples/bluetooth/nimble/ble_spp/spp_server/main/main.c
Normal file
443
examples/bluetooth/nimble/ble_spp/spp_server/main/main.c
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "esp_nimble_hci.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "ble_spp_server.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
static const char *tag = "NimBLE_SPP_BLE_PRPH";
|
||||
static int ble_spp_server_gap_event(struct ble_gap_event *event, void *arg);
|
||||
static uint8_t own_addr_type;
|
||||
int gatt_svr_register(void);
|
||||
QueueHandle_t spp_common_uart_queue = NULL;
|
||||
static bool is_connect = false;
|
||||
uint16_t connection_handle;
|
||||
static uint16_t ble_svc_gatt_read_val_handle,ble_spp_svc_gatt_read_val_handle;
|
||||
|
||||
/* 16 Bit Alert Notification Service UUID */
|
||||
#define BLE_SVC_ANS_UUID16 0x1811
|
||||
|
||||
/* 16 Bit Alert Notification Service Characteristic UUIDs */
|
||||
#define BLE_SVC_ANS_CHR_UUID16_SUP_NEW_ALERT_CAT 0x2a47
|
||||
|
||||
/* 16 Bit SPP Service UUID */
|
||||
#define BLE_SVC_SPP_UUID16 0xABF0
|
||||
|
||||
/* 16 Bit SPP Service Characteristic UUID */
|
||||
#define BLE_SVC_SPP_CHR_UUID16 0xABF1
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
/**
|
||||
* Logs information about a connection to the console.
|
||||
*/
|
||||
static void
|
||||
ble_spp_server_print_conn_desc(struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
|
||||
desc->conn_handle, desc->our_ota_addr.type);
|
||||
print_addr(desc->our_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
|
||||
desc->our_id_addr.type);
|
||||
print_addr(desc->our_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
|
||||
desc->peer_ota_addr.type);
|
||||
print_addr(desc->peer_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
|
||||
desc->peer_id_addr.type);
|
||||
print_addr(desc->peer_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
||||
"encrypted=%d authenticated=%d bonded=%d\n",
|
||||
desc->conn_itvl, desc->conn_latency,
|
||||
desc->supervision_timeout,
|
||||
desc->sec_state.encrypted,
|
||||
desc->sec_state.authenticated,
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables advertising with the following parameters:
|
||||
* o General discoverable mode.
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
ble_spp_server_advertise(void)
|
||||
{
|
||||
struct ble_gap_adv_params adv_params;
|
||||
struct ble_hs_adv_fields fields;
|
||||
const char *name;
|
||||
int rc;
|
||||
|
||||
/**
|
||||
* Set the advertisement data included in our advertisements:
|
||||
* o Flags (indicates advertisement type and other general info).
|
||||
* o Advertising tx power.
|
||||
* o Device name.
|
||||
* o 16-bit service UUIDs (alert notifications).
|
||||
*/
|
||||
|
||||
memset(&fields, 0, sizeof fields);
|
||||
|
||||
/* Advertise two flags:
|
||||
* o Discoverability in forthcoming advertisement (general)
|
||||
* o BLE-only (BR/EDR unsupported).
|
||||
*/
|
||||
fields.flags = BLE_HS_ADV_F_DISC_GEN |
|
||||
BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
|
||||
/* Indicate that the TX power level field should be included; have the
|
||||
* stack fill this value automatically. This is done by assigning the
|
||||
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
|
||||
*/
|
||||
fields.tx_pwr_lvl_is_present = 1;
|
||||
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||
|
||||
name = ble_svc_gap_device_name();
|
||||
fields.name = (uint8_t *)name;
|
||||
fields.name_len = strlen(name);
|
||||
fields.name_is_complete = 1;
|
||||
|
||||
fields.uuids16 = (ble_uuid16_t[]) {
|
||||
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
|
||||
};
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
|
||||
rc = ble_gap_adv_set_fields(&fields);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Begin advertising. */
|
||||
memset(&adv_params, 0, sizeof adv_params);
|
||||
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
|
||||
&adv_params, ble_spp_server_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The nimble host executes this callback when a GAP event occurs. The
|
||||
* application associates a GAP event callback with each connection that forms.
|
||||
* ble_spp_server uses the same callback for all connections.
|
||||
*
|
||||
* @param event The type of event being signalled.
|
||||
* @param ctxt Various information pertaining to the event.
|
||||
* @param arg Application-specified argument; unused by
|
||||
* ble_spp_server.
|
||||
*
|
||||
* @return 0 if the application successfully handled the
|
||||
* event; nonzero on failure. The semantics
|
||||
* of the return code is specific to the
|
||||
* particular GAP event being signalled.
|
||||
*/
|
||||
static int
|
||||
ble_spp_server_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
MODLOG_DFLT(INFO, "connection %s; status=%d ",
|
||||
event->connect.status == 0 ? "established" : "failed",
|
||||
event->connect.status);
|
||||
if (event->connect.status == 0) {
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_spp_server_print_conn_desc(&desc);
|
||||
is_connect=true;
|
||||
connection_handle = event->connect.conn_handle;
|
||||
}
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
if (event->connect.status != 0) {
|
||||
/* Connection failed; resume advertising. */
|
||||
ble_spp_server_advertise();
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
|
||||
ble_spp_server_print_conn_desc(&event->disconnect.conn);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* Connection terminated; resume advertising. */
|
||||
ble_spp_server_advertise();
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
/* The central has updated the connection parameters. */
|
||||
MODLOG_DFLT(INFO, "connection updated; status=%d ",
|
||||
event->conn_update.status);
|
||||
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_spp_server_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
|
||||
event->adv_complete.reason);
|
||||
ble_spp_server_advertise();
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_MTU:
|
||||
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ble_spp_server_on_reset(int reason)
|
||||
{
|
||||
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
static void
|
||||
ble_spp_server_on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
/* Figure out address to use while advertising (no privacy for now) */
|
||||
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Printing ADDR */
|
||||
uint8_t addr_val[6] = {0};
|
||||
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr_val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
/* Begin advertising. */
|
||||
ble_spp_server_advertise();
|
||||
}
|
||||
|
||||
void ble_spp_server_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(tag, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
/* Callback function for custom service */
|
||||
static int ble_svc_gatt_handler(uint16_t conn_handle, uint16_t attr_handle,struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
switch(ctxt->op){
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||
ESP_LOGI(tag, "Callback for read");
|
||||
break;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||
ESP_LOGI(tag,"Data received in write event,conn_handle = %x,attr_handle = %x",conn_handle,attr_handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGI(tag, "\nDefault Callback");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Define new custom service */
|
||||
static const struct ble_gatt_svc_def new_ble_svc_gatt_defs[] = {
|
||||
{
|
||||
/*** Service: GATT */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = BLE_UUID16_DECLARE(BLE_SVC_ANS_UUID16),
|
||||
.characteristics = (struct ble_gatt_chr_def[]) { {
|
||||
/* Support new alert category */
|
||||
.uuid = BLE_UUID16_DECLARE(BLE_SVC_ANS_CHR_UUID16_SUP_NEW_ALERT_CAT),
|
||||
.access_cb = ble_svc_gatt_handler,
|
||||
.val_handle = &ble_svc_gatt_read_val_handle,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
},
|
||||
{
|
||||
0, /* No more characteristics */
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
/*** Service: SPP */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = BLE_UUID16_DECLARE(BLE_SVC_SPP_UUID16),
|
||||
.characteristics = (struct ble_gatt_chr_def[]) { {
|
||||
/* Support SPP service */
|
||||
.uuid = BLE_UUID16_DECLARE(BLE_SVC_SPP_CHR_UUID16),
|
||||
.access_cb = ble_svc_gatt_handler,
|
||||
.val_handle = &ble_spp_svc_gatt_read_val_handle,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
},
|
||||
{
|
||||
0, /* No more characteristics */
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
|
||||
int gatt_svr_register(void)
|
||||
{
|
||||
int rc=0;
|
||||
|
||||
rc = ble_gatts_count_cfg(new_ble_svc_gatt_defs);
|
||||
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ble_gatts_add_svcs(new_ble_svc_gatt_defs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ble_server_uart_task(void *pvParameters){
|
||||
ESP_LOGI(tag,"BLE server UART_task started\n");
|
||||
uart_event_t event;
|
||||
int rc=0;
|
||||
for (;;) {
|
||||
//Waiting for UART event.
|
||||
if (xQueueReceive(spp_common_uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
|
||||
switch (event.type) {
|
||||
//Event of UART receving data
|
||||
case UART_DATA:
|
||||
if (event.size && (is_connect == true)) {
|
||||
static uint8_t ntf[1];
|
||||
ntf[0] = 90;
|
||||
struct os_mbuf *txom;
|
||||
txom = ble_hs_mbuf_from_flat(ntf, sizeof(ntf));
|
||||
rc = ble_gattc_notify_custom(connection_handle,ble_spp_svc_gatt_read_val_handle,txom);
|
||||
if( rc == 0){
|
||||
ESP_LOGI(tag,"Notification sent successfully");
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(tag,"Error in sending notification");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
static void ble_spp_uart_init(void)
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_RTS,
|
||||
.rx_flow_ctrl_thresh = 122,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
//Install UART driver, and get the queue.
|
||||
uart_driver_install(UART_NUM_0, 4096, 8192, 10,&spp_common_uart_queue,0);
|
||||
//Set UART parameters
|
||||
uart_param_config(UART_NUM_0, &uart_config);
|
||||
//Set UART pins
|
||||
uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
xTaskCreate(ble_server_uart_task, "uTask", 2048, (void*)UART_NUM_0, 8, NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
|
||||
|
||||
nimble_port_init();
|
||||
|
||||
/* Initialize uart driver and start uart task */
|
||||
ble_spp_uart_init();
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = ble_spp_server_on_reset;
|
||||
ble_hs_cfg.sync_cb = ble_spp_server_on_sync;
|
||||
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
|
||||
#ifdef CONFIG_EXAMPLE_BONDING
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_MITM
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_USE_SC
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
#else
|
||||
ble_hs_cfg.sm_sc = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_BONDING
|
||||
ble_hs_cfg.sm_our_key_dist = 1;
|
||||
ble_hs_cfg.sm_their_key_dist = 1;
|
||||
#endif
|
||||
|
||||
|
||||
rc = new_gatt_svr_init();
|
||||
assert(rc == 0);
|
||||
|
||||
/* Register custom service */
|
||||
rc = gatt_svr_register();
|
||||
assert(rc == 0);
|
||||
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble-ble-spp-svr");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(ble_spp_server_host_task);
|
||||
}
|
30
examples/bluetooth/nimble/ble_spp/spp_server/main/misc.c
Normal file
30
examples/bluetooth/nimble/ble_spp/spp_server/main/misc.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "ble_spp_server.h"
|
||||
|
||||
/**
|
||||
* Utility function to log an array of bytes.
|
||||
*/
|
||||
void
|
||||
print_bytes(const uint8_t *bytes, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_addr(const void *addr)
|
||||
{
|
||||
const uint8_t *u8p;
|
||||
|
||||
u8p = addr;
|
||||
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
Reference in New Issue
Block a user