DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ble_gatt.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ble_gatt.cpp
6 * @brief Bluetooth ATT protocol codec + GATT characteristic bridge (see ble_gatt.h).
7 */
8
10
11#if DETWS_ENABLE_BLE_GATT
12
13#include <string.h>
14
15size_t att_read_req(uint16_t handle, uint8_t *out, size_t cap)
16{
17 if (!out || cap < 3)
18 return 0;
19 out[0] = AttOp::ATT_OP_READ_REQ;
20 out[1] = (uint8_t)handle;
21 out[2] = (uint8_t)(handle >> 8);
22 return 3;
23}
24
25size_t att_read_rsp(const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
26{
27 if (!out || (vlen && !val) || cap < 1 + vlen)
28 return 0;
29 out[0] = AttOp::ATT_OP_READ_RSP;
30 if (vlen)
31 memcpy(out + 1, val, vlen);
32 return 1 + vlen;
33}
34
35static size_t att_handle_value(uint8_t op, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
36{
37 if (!out || (vlen && !val) || cap < 3 + vlen)
38 return 0;
39 out[0] = op;
40 out[1] = (uint8_t)handle;
41 out[2] = (uint8_t)(handle >> 8);
42 if (vlen)
43 memcpy(out + 3, val, vlen);
44 return 3 + vlen;
45}
46
47size_t att_write_req(uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
48{
49 return att_handle_value(AttOp::ATT_OP_WRITE_REQ, handle, val, vlen, out, cap);
50}
51
52size_t att_notify(uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
53{
54 return att_handle_value(AttOp::ATT_OP_HANDLE_VALUE_NTF, handle, val, vlen, out, cap);
55}
56
57size_t att_error_rsp(uint8_t req_op, uint16_t handle, uint8_t error, uint8_t *out, size_t cap)
58{
59 if (!out || cap < 5)
60 return 0;
61 out[0] = AttOp::ATT_OP_ERROR_RSP;
62 out[1] = req_op;
63 out[2] = (uint8_t)handle;
64 out[3] = (uint8_t)(handle >> 8);
65 out[4] = error;
66 return 5;
67}
68
69bool att_parse(const uint8_t *pdu, size_t len, AttPdu *out)
70{
71 if (!pdu || !out || len < 1)
72 return false;
73 out->opcode = pdu[0];
74 out->handle = 0;
75 out->req_op = 0;
76 out->error = 0;
77 out->value = nullptr;
78 out->value_len = 0;
79
80 switch (pdu[0])
81 {
82 case AttOp::ATT_OP_ERROR_RSP:
83 if (len < 5)
84 return false;
85 out->req_op = pdu[1];
86 out->handle = (uint16_t)(pdu[2] | (pdu[3] << 8));
87 out->error = pdu[4];
88 return true;
89 case AttOp::ATT_OP_READ_REQ:
90 if (len < 3)
91 return false;
92 out->handle = (uint16_t)(pdu[1] | (pdu[2] << 8));
93 return true;
94 case AttOp::ATT_OP_READ_RSP:
95 if (len > 1)
96 {
97 out->value = pdu + 1;
98 out->value_len = len - 1;
99 }
100 return true;
101 case AttOp::ATT_OP_WRITE_REQ:
102 case AttOp::ATT_OP_HANDLE_VALUE_NTF:
103 if (len < 3)
104 return false;
105 out->handle = (uint16_t)(pdu[1] | (pdu[2] << 8));
106 if (len > 3)
107 {
108 out->value = pdu + 3;
109 out->value_len = len - 3;
110 }
111 return true;
112 case AttOp::ATT_OP_WRITE_RSP:
113 return true;
114 default:
115 return true; // unknown opcode: still report it, no fixed fields
116 }
117}
118
119namespace
120{
121struct Buf
122{
123 char *p;
124 size_t cap;
125 size_t len;
126 bool ok;
127};
128
129void put(Buf *b, const char *s)
130{
131 if (!b->ok)
132 return;
133 size_t sl = strnlen(s, b->cap + 1);
134 if (b->len + sl >= b->cap)
135 {
136 b->ok = false;
137 return;
138 }
139 memcpy(b->p + b->len, s, sl);
140 b->len += sl;
141}
142
143void put_u(Buf *b, uint32_t v)
144{
145 char t[10];
146 int n = 0;
147 do
148 {
149 t[n++] = (char)('0' + v % 10);
150 v /= 10;
151 } while (v);
152 char o[11];
153 for (int i = 0; i < n; i++)
154 o[i] = t[n - 1 - i];
155 o[n] = '\0';
156 put(b, o);
157}
158
159void put_hex16(Buf *b, uint16_t v)
160{
161 char t[7] = "0x0000";
162 static const char *H = "0123456789abcdef";
163 for (int i = 0; i < 4; i++)
164 t[2 + i] = H[(v >> ((3 - i) * 4)) & 0xF];
165 put(b, t);
166}
167} // namespace
168
169size_t gatt_char_json(const GattChar *chars, size_t n, char *out, size_t cap)
170{
171 if (!out || cap == 0 || (n && !chars))
172 return 0;
173 Buf b = {out, cap, 0, true};
174 put(&b, "[");
175 for (size_t i = 0; i < n; i++)
176 {
177 if (i)
178 put(&b, ",");
179 put(&b, "{\"handle\":");
180 put_u(&b, chars[i].handle);
181 put(&b, ",\"uuid\":\"");
182 put_hex16(&b, chars[i].uuid);
183 put(&b, "\",\"props\":");
184 put_u(&b, chars[i].props);
185 put(&b, "}");
186 }
187 put(&b, "]");
188 if (!b.ok)
189 return 0;
190 out[b.len] = '\0';
191 return b.len;
192}
193
194#endif // DETWS_ENABLE_BLE_GATT
Bluetooth ATT protocol codec + GATT characteristic bridge (DETWS_ENABLE_BLE_GATT).