Skip to content

Commit ba2652a

Browse files
committed
samples: bluetooth: peripheral_gap_svc: Adding sample
Adding a sample that presents how to implement non default GAP service inside the application. Signed-off-by: Radosław Koppel <[email protected]>
1 parent 636a4d5 commit ba2652a

File tree

5 files changed

+221
-0
lines changed

5 files changed

+221
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cmake_minimum_required(VERSION 3.20.0)
2+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
3+
project(peripheral_gap_svc)
4+
5+
target_sources(app PRIVATE
6+
src/main.c
7+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.. zephyr:code-sample:: ble_peripheral_gap_svc
2+
:name: Peripheral GAP Server non default implementation
3+
:relevant-api: bluetooth
4+
5+
Implement a dummy peripheral with GAP Server that limits the accepted names
6+
7+
Overview
8+
********
9+
10+
This sample demonstrates the implementation of the GAP service
11+
12+
13+
14+
15+
This sample demonstrates the usage of the NUS service (Nordic UART Service) as a serial
16+
endpoint to exchange data. In this case, the sample assumes the data is UTF-8 encoded,
17+
but it may be binary data. Once the user connects to the device and subscribes to the TX
18+
characteristic, it will start receiving periodic notifications with "Hello World!\n".
19+
20+
Requirements
21+
************
22+
23+
* BlueZ running on the host, or
24+
* A board with Bluetooth LE support
25+
26+
Building and Running
27+
********************
28+
29+
See :zephyr:code-sample-category:`bluetooth` samples for details.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CONFIG_LOG=y
2+
CONFIG_BT=y
3+
CONFIG_BT_SMP=y
4+
CONFIG_BT_PERIPHERAL=y
5+
6+
# Testing locally redefined SVC implementation
7+
CONFIG_BT_GAP_SVC_DEFAULT_IMPL=n
8+
9+
CONFIG_BT_DEVICE_NAME="Zephyr GAP service"
10+
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
11+
CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y
12+
13+
CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=n
14+
CONFIG_BT_DEVICE_APPEARANCE_GATT_WRITABLE=n
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
sample:
2+
name: Bluetooth Peripheral GAP service non default implementation
3+
description: Demonstrates the GAP service implementation on application side.
4+
tests:
5+
sample.bluetooth.peripheral_gap_svc:
6+
harness: bluetooth
7+
platform_allow:
8+
- qemu_cortex_m3
9+
- qemu_x86
10+
- nrf52840dk/nrf52840
11+
- ophelia4ev/nrf54l15/cpuapp
12+
integration_platforms:
13+
- qemu_cortex_m3
14+
tags: bluetooth
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) 2025 Koppel Electronic
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/bluetooth/bluetooth.h>
9+
#include <zephyr/sys/byteorder.h>
10+
#include <zephyr/bluetooth/gatt.h>
11+
12+
13+
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
14+
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
15+
16+
static const struct bt_data ad[] = {
17+
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
18+
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
19+
};
20+
21+
/* -----------------------------------------------------------------------------
22+
* Local implementation of GAP service
23+
*/
24+
25+
static ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
26+
void *buf, uint16_t len, uint16_t offset)
27+
{
28+
const char *name = bt_get_name();
29+
30+
return bt_gatt_attr_read(conn, attr, buf, len, offset, name,
31+
strlen(name));
32+
}
33+
34+
static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
35+
uint16_t len, uint16_t offset, uint8_t flags)
36+
{
37+
/* adding one to fit the terminating null character */
38+
char value[CONFIG_BT_DEVICE_NAME_MAX + 1] = {};
39+
40+
if (offset != 0) {
41+
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
42+
}
43+
44+
if (offset + len > CONFIG_BT_DEVICE_NAME_MAX) {
45+
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
46+
}
47+
48+
memcpy(value, buf, len);
49+
50+
value[len] = '\0';
51+
52+
/* Check if the name starts with capital letter */
53+
if (value[0] < 'A' || value[0] > 'Z') {
54+
printk("Rejected name change to \"%s\": must start with capital letter\n", value);
55+
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
56+
}
57+
58+
bt_set_name(value);
59+
60+
printk("Name changed to \"%s\"\n", value);
61+
62+
return len;
63+
}
64+
65+
static ssize_t read_appearance(struct bt_conn *conn,
66+
const struct bt_gatt_attr *attr, void *buf,
67+
uint16_t len, uint16_t offset)
68+
{
69+
uint16_t appearance = sys_cpu_to_le16(bt_get_appearance());
70+
71+
return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance, sizeof(appearance));
72+
}
73+
74+
BT_GATT_SERVICE_DEFINE(BT_GATT_GAP_SVC_DEFAULT_NAME,
75+
BT_GATT_PRIMARY_SERVICE(BT_UUID_GAP),
76+
/* Require pairing for writes to device name */
77+
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME,
78+
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
79+
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
80+
read_name, write_name, NULL),
81+
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_APPEARANCE,
82+
BT_GATT_CHRC_READ,
83+
BT_GATT_PERM_READ,
84+
read_appearance, NULL, NULL),
85+
);
86+
87+
/* End of local implementation of GAP service
88+
* ---------------------------------------------------------------------------
89+
*/
90+
91+
static void connected(struct bt_conn *conn, uint8_t conn_err)
92+
{
93+
char addr[BT_ADDR_LE_STR_LEN];
94+
95+
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
96+
97+
if (conn_err) {
98+
printk("Failed to connect to %s (%u)\n", addr, conn_err);
99+
return;
100+
}
101+
102+
printk("Connected: %s\n", addr);
103+
}
104+
105+
static void disconnected(struct bt_conn *conn, uint8_t reason)
106+
{
107+
char addr[BT_ADDR_LE_STR_LEN];
108+
109+
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
110+
111+
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
112+
}
113+
114+
static int start_advertising(void)
115+
{
116+
int err;
117+
118+
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
119+
if (err) {
120+
printk("Advertising failed to start (err %d)\n", err);
121+
}
122+
123+
return err;
124+
}
125+
126+
static void recycled(void)
127+
{
128+
start_advertising();
129+
}
130+
131+
BT_CONN_CB_DEFINE(conn_callbacks) = {
132+
.connected = connected,
133+
.disconnected = disconnected,
134+
.recycled = recycled,
135+
};
136+
137+
int main(void)
138+
{
139+
int err;
140+
141+
printk("Sample - Bluetooth Peripheral GAP service\n");
142+
143+
err = bt_enable(NULL);
144+
if (err) {
145+
printk("Failed to enable bluetooth: %d\n", err);
146+
return err;
147+
}
148+
149+
err = start_advertising();
150+
if (err) {
151+
return err;
152+
}
153+
154+
printk("Initialization complete\n");
155+
156+
return 0;
157+
}

0 commit comments

Comments
 (0)