ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ble_gatt.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#ifndef PROTOCORE_BLE_GATT_H
5#define PROTOCORE_BLE_GATT_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file ble_gatt.h
13 * @brief Bluetooth ATT protocol codec + GATT characteristic bridge (PROTOCORE_ENABLE_BLE_GATT).
14 *
15 * The ESP32's BLE radio is on-chip, but bridging GATT to the web still needs the wire protocol under
16 * GATT - the **Attribute Protocol** (ATT, Bluetooth Core Vol 3 Part F): the read / write / notify /
17 * error PDUs a central and peripheral exchange, each a 1-byte opcode followed by a little-endian
18 * attribute handle and value. This is that codec (build + parse the common ATT PDUs) plus a small
19 * characteristic table serializer that exposes discovered / offered GATT characteristics as JSON for the
20 * web stack.
21 *
22 * Pure, zero heap, no stdlib, host-testable. The BLE stack (NimBLE / Bluedroid) owns the radio; this owns
23 * the ATT bytes and the northbound JSON.
24 *
25 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
26 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
27 * a caller drives every namespace the same way.
28 */
29
30// PROTOCORE_BLE_GATT_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
31// it into its arena. Its size and its offset are each a static_assert, so a feature
32// combination that does not fit fails to compile rather than overrunning at run time.
33
34/** @brief ATT opcodes (subset). */
35#define ATT_OP_ERROR_RSP 0x01 ///< [op][req-op][handle:2][error]
36#define ATT_OP_READ_REQ 0x0A ///< [op][handle:2]
37#define ATT_OP_READ_RSP 0x0B ///< [op][value...]
38#define ATT_OP_WRITE_REQ 0x12 ///< [op][handle:2][value...]
39#define ATT_OP_WRITE_RSP 0x13 ///< [op]
40#define ATT_OP_HANDLE_VALUE_NTF 0x1B ///< [op][handle:2][value...]
41
42/** @brief GATT characteristic property bits (declaration properties byte). */
43#define GATT_PROP_READ 0x02
44#define GATT_PROP_WRITE_NR 0x04 ///< write without response.
45#define GATT_PROP_WRITE 0x08
46#define GATT_PROP_NOTIFY 0x10
47#define GATT_PROP_INDICATE 0x20
48
49/** @brief A parsed ATT PDU (value points into the input). */
50typedef struct
51{
52 uint8_t opcode;
53 uint16_t handle; ///< set for opcodes that carry a handle (else 0).
54 uint8_t req_op; ///< for ERROR_RSP: the failed request opcode.
55 uint8_t error; ///< for ERROR_RSP: the error code.
56 const uint8_t *value; ///< value payload (null if none).
57 size_t value_len;
58} AttPdu;
59
60/** @brief One GATT characteristic for the northbound bridge. */
61typedef struct
62{
63 uint16_t handle;
64 uint16_t uuid; ///< 16-bit UUID (assigned-number form).
65 uint8_t props; ///< GATT_PROP_* bits.
66} GattChar;
67
68/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
69typedef struct
70{
71 size_t (*att_read_req)(uint8_t *, uint16_t, uint8_t *, size_t);
72 size_t (*att_read_rsp)(uint8_t *, const uint8_t *, size_t, uint8_t *, size_t);
73 size_t (*att_write_req)(uint8_t *, uint16_t, const uint8_t *, size_t, uint8_t *, size_t);
74 size_t (*att_notify)(uint8_t *, uint16_t, const uint8_t *, size_t, uint8_t *, size_t);
75 size_t (*att_error_rsp)(uint8_t *, uint8_t, uint16_t, uint8_t, uint8_t *, size_t);
76 proto_bool (*att_parse)(uint8_t *, const uint8_t *, size_t, AttPdu *);
77 size_t (*char_json)(uint8_t *, const GattChar *, size_t, char *, size_t);
78} BleGattNs;
79PROTOCORE_NS_LAYOUT(BleGattNs, att_read_req, att_read_rsp, att_write_req, att_notify, att_error_rsp, att_parse,
80 char_json);
81
82/**
83 * @brief Build a Read Request: [0x0A][handle:2 LE]. 3, or 0 on overflow.
84 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
85 * @param handle Handle
86 * @param out Out
87 * @param cap Cap
88 * @return The size_t.
89 */
90size_t protocore_ble_gatt_att_read_req(uint8_t *work, uint16_t handle, uint8_t *out, size_t cap);
91/**
92 * @brief Build a Read Response: [0x0B][value...]. 1+vlen, or 0 on overflow.
93 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
94 * @param val Val
95 * @param vlen Vlen
96 * @param out Out
97 * @param cap Cap
98 * @return The size_t.
99 */
100size_t protocore_ble_gatt_att_read_rsp(uint8_t *work, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap);
101/**
102 * @brief Build a Write Request: [0x12][handle:2 LE][value...]. 3+vlen, or 0 .
103 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
104 * @param handle Handle
105 * @param val Val
106 * @param vlen Vlen
107 * @param out Out
108 * @param cap Cap
109 * @return The size_t.
110 */
111size_t protocore_ble_gatt_att_write_req(uint8_t *work, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out,
112 size_t cap);
113/**
114 * @brief Build a Handle Value Notification: [0x1B][handle:2 LE][value...]. .
115 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
116 * @param handle Handle
117 * @param val Val
118 * @param vlen Vlen
119 * @param out Out
120 * @param cap Cap
121 * @return The size_t.
122 */
123size_t protocore_ble_gatt_att_notify(uint8_t *work, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out,
124 size_t cap);
125/**
126 * @brief Build an Error Response: [0x01][req-op][handle:2 LE][error]. 5, or .
127 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
128 * @param req_op Req op
129 * @param handle Handle
130 * @param error Error
131 * @param out Out
132 * @param cap Cap
133 * @return The size_t.
134 */
135size_t protocore_ble_gatt_att_error_rsp(uint8_t *work, uint8_t req_op, uint16_t handle, uint8_t error, uint8_t *out,
136 size_t cap);
137/**
138 * @brief Parse an ATT PDU into out. true if len >= 1 and the fixed fields fit.
139 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
140 * @param pdu Pdu
141 * @param len Len
142 * @param out Out
143 * @return PROTO_TRUE on success.
144 */
145proto_bool protocore_ble_gatt_att_parse(uint8_t *work, const uint8_t *pdu, size_t len, AttPdu *out);
146/**
147 * @brief Serialize a characteristic table as .
148 * @param work PROTOCORE_BLE_GATT_BORROW bytes the caller took. Not held past the call.
149 * @param chars Chars
150 * @param n N
151 * @param out Out
152 * @param cap Cap
153 * @return The size_t.
154 */
155size_t protocore_ble_gatt_char_json(uint8_t *work, const GattChar *chars, size_t n, char *out, size_t cap);
156
157/** @brief Module namespace. */
165
167
168#endif // PROTOCORE_BLE_GATT_H
size_t protocore_ble_gatt_char_json(uint8_t *work, const GattChar *chars, size_t n, char *out, size_t cap)
Serialize a characteristic table as .
size_t protocore_ble_gatt_att_error_rsp(uint8_t *work, uint8_t req_op, uint16_t handle, uint8_t error, uint8_t *out, size_t cap)
Build an Error Response: [0x01][req-op][handle:2 LE][error]. 5, or .
size_t protocore_ble_gatt_att_write_req(uint8_t *work, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
Build a Write Request: [0x12][handle:2 LE][value...]. 3+vlen, or 0 .
proto_bool protocore_ble_gatt_att_parse(uint8_t *work, const uint8_t *pdu, size_t len, AttPdu *out)
Parse an ATT PDU into out. true if len >= 1 and the fixed fields fit.
size_t protocore_ble_gatt_att_read_rsp(uint8_t *work, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
Build a Read Response: [0x0B][value...]. 1+vlen, or 0 on overflow.
PROTOCORE_NS BleGattNs BleGatt PROTOCORE_UNUSED
Module namespace.
Definition ble_gatt.h:158
size_t protocore_ble_gatt_att_notify(uint8_t *work, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
Build a Handle Value Notification: [0x1B][handle:2 LE][value...]. .
size_t protocore_ble_gatt_att_read_req(uint8_t *work, uint16_t handle, uint8_t *out, size_t cap)
Build a Read Request: [0x0A][handle:2 LE]. 3, or 0 on overflow.
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
A parsed ATT PDU (value points into the input).
Definition ble_gatt.h:51
uint16_t handle
set for opcodes that carry a handle (else 0).
Definition ble_gatt.h:53
const uint8_t * value
value payload (null if none).
Definition ble_gatt.h:56
uint8_t error
for ERROR_RSP: the error code.
Definition ble_gatt.h:55
uint8_t opcode
Definition ble_gatt.h:52
uint8_t req_op
for ERROR_RSP: the failed request opcode.
Definition ble_gatt.h:54
size_t value_len
Definition ble_gatt.h:57
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition ble_gatt.h:70
size_t(* att_read_req)(uint8_t *, uint16_t, uint8_t *, size_t)
Definition ble_gatt.h:71
One GATT characteristic for the northbound bridge.
Definition ble_gatt.h:62
uint16_t handle
Definition ble_gatt.h:63
uint16_t uuid
16-bit UUID (assigned-number form).
Definition ble_gatt.h:64
uint8_t props
GATT_PROP_* bits.
Definition ble_gatt.h:65
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97