kindlebt
Bluetooth functionality for Kindle 11th gen and up
Loading...
Searching...
No Matches
basic_usage.c
1#include <stdio.h>
2#include <unistd.h>
3
4#include <kindlebt/kindlebt.h>
5#include <kindlebt/kindlebt_log.h>
6
7static sessionHandle bt_session = NULL;
8static bleConnHandle conn_handle = NULL;
9static bleGattsService_t gatt_db;
10
11// For our dumb callback waits implementation
12static bool read_characteristic = false;
13static bool wrote_characteristic = false;
14
15// ===== UTILS =====
16void print_uuid(uuid_t* uuid) {
17 printf(
18 "UUID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
19 uuid->uu[0], uuid->uu[1], uuid->uu[2], uuid->uu[3], uuid->uu[4], uuid->uu[5], uuid->uu[5],
20 uuid->uu[7], uuid->uu[8], uuid->uu[9], uuid->uu[10], uuid->uu[11], uuid->uu[12],
21 uuid->uu[13], uuid->uu[14], uuid->uu[15]
22 );
23}
24
25// This is a very simple implementation. In some cases, the response might not even be
26// a @ref bleGattBlobValue_t like we're assuming here
27// Another thing to note is that the \c data field is just a \c uint8_t* field. If you
28// expect text, you'll need to cast it with an ASCII table
29void print_data(bleGattBlobValue_t* value) {
30 printf("Data: ");
31 for (int idx = 0; idx < value->size; idx++)
32 printf("%02x ", value->data[idx]);
33 printf("\n");
34}
35
36void freeGattBlob(bleGattCharacteristicsValue_t* chars_value) {
37 if (chars_value == NULL || chars_value->blobValue.data == NULL) return;
38
39 free(chars_value->blobValue.data);
40 chars_value->blobValue.data = NULL;
41 chars_value->blobValue.size = 0;
42 chars_value->blobValue.offset = 0;
43}
44
45void setGattBlobFromBytes(
46 bleGattCharacteristicsValue_t* chars_value, const uint8_t* data, uint16_t size
47) {
48 if (chars_value == NULL || data == NULL || size == 0) return;
49
50 free(chars_value->blobValue.data);
51
52 uint8_t* blob = malloc(size);
53 if (blob == NULL) return;
54
55 memcpy(blob, data, size);
56
57 chars_value->blobValue.data = blob;
58 chars_value->blobValue.size = size;
59 chars_value->blobValue.offset = 0;
60 chars_value->format = BLE_FORMAT_BLOB;
61}
62
63// ===== CALLBACKS =====
64void bleGattcNotifyCharsCallback(
65 bleConnHandle conn_handle, bleGattCharacteristicsValue_t chars_value
66) {
67 printf("Callback %s(): conn_handle %p", __func__, conn_handle);
68 print_uuid(&chars_value.gattRecord.uuid);
69 print_data(&chars_value.blobValue);
70}
71
72void bleGattcReadCharsCallback(
73 bleConnHandle conn_handle, bleGattCharacteristicsValue_t chars_value, status_t status
74) {
75 printf("Callback %s(): status %d conn_handle %p\n", __func__, status, conn_handle);
76 print_uuid(&chars_value.gattRecord.uuid);
77 print_data(&chars_value.blobValue);
78 read_characteristic = true;
79}
80
81void bleGattcWriteCharsCallback(
82 bleConnHandle conn_handle, bleGattCharacteristicsValue_t gatt_characteristics, status_t status
83) {
84 printf("Callback %s(): conn_handle %p status %d", __func__, conn_handle, status);
85 wrote_characteristic = true;
86}
87
88static bleGattClientCallbacks_t gatt_app_callbacks = {
89 .size = sizeof(bleGattClientCallbacks_t),
90 .notify_characteristics_cb = bleGattcNotifyCharsCallback,
91 .on_ble_gattc_read_characteristics_cb = bleGattcReadCharsCallback,
92 .on_ble_gattc_write_characteristics_cb = bleGattcWriteCharsCallback,
93};
94
95int main() {
96 // (OPTIONAL) SET LOG LEVEL
97 kindlebt_set_log_level(LOG_LEVEL_DEBUG);
98
99 // USE BLUETOOTH PRIVILEGES
100 // The ace_bt stuff won't run under root user
101 if (setgid((gid_t)1003) || setuid((uid_t)1003)) {
102 fprintf(stderr, "Can't drop privileges to bluetooth user/group\n");
103 return -1;
104 }
105
106 printf("Hello World from Kindle!\n");
107
108 // CHECK FOR BLE SUPPORT
109 // Might not be necessary
110 bool isBLE = isBLESupported();
111 printf("Is BLE enabled: %d\n", isBLE);
112 if (!isBLE) {
113 fprintf(stderr, "BLE is not enabled\n");
114 return -2;
115 }
116
117 status_t status;
118
119 // OPEN SESSION
120 // Necessary, basically all API operations require a BT session
121 status = openSession(ACEBT_SESSION_TYPE_DUAL_MODE, &bt_session);
122 printf(
123 "Opened session status %d, session %p (u32 %u)\n", status, bt_session, (uint32_t)bt_session
124 );
125 if (status != ACE_STATUS_OK) {
126 fprintf(stderr, "Cannot open Bluetooth session, status: %d\n", status);
127 return -3;
128 }
129
130 // REGISTER FOR BLE USE
131 // This registers for BLE and related callbacks functionality in btmanagerd
132 status = bleRegister(bt_session);
133 printf("Registered BLE: %d\n", status);
134 if (status != ACE_STATUS_OK) {
135 fprintf(stderr, "Cannot register BLE, status: %d\n", status);
136 return -4;
137 }
138
139 // REGISTER FOR GATT CLIENT USE
140 // Likewise, same as before but specifically for GATTC events in btmanagerd
141 status = bleRegisterGattClient(bt_session, &gatt_app_callbacks);
142 printf("Registered GATT Client status: %d\n", status);
143 if (status != ACE_STATUS_OK) {
144 fprintf(stderr, "Cannot register GATT Client, status: %d\n", status);
145 return -5;
146 }
147
148 // CONNECT TO BLE DEVICE
149 // Untested whether connecting to a Classic device with this API works
150 bdAddr_t ble_addr = {.address = {0x2C, 0xCF, 0x67, 0xB8, 0xDC, 0x3F}};
151 status = bleConnect(
152 bt_session, &conn_handle, &ble_addr, ACE_BT_BLE_CONN_PARAM_BALANCED,
153 ACEBT_BLE_GATT_CLIENT_ROLE, ACE_BT_BLE_CONN_PRIO_MEDIUM
154 );
155 printf("Connected to BLE status:%d\n", status);
156 if (status != ACE_STATUS_OK) {
157 fprintf(stderr, "Cannot connect to BLE device, status: %d\n", status);
158 return -6;
159 }
160
161 // DISCOVER GATT SERVICES
162 // Necessary step to discover all the GATT services of the remote device
163 status = bleDiscoverAllServices(bt_session, conn_handle);
164 printf("Discovered all services: %d\n", status);
165 if (status != ACE_STATUS_OK) {
166 fprintf(stderr, "Cannot discover services of BLE device, status: %d\n", status);
167 return -7;
168 }
169
170 // RETRIEVE GATT SERVICES (DB)
171 // Retrieval of services is a different and also necessary step from discovery
172 // Even if you know the Characteristic UUIDs ahead of time, you still need to
173 // discover and retrieve the services
174 status = bleGetDatabase(conn_handle, &gatt_db);
175 printf("Requested GATT DB status: %d\n", status);
176 if (status != ACE_STATUS_OK) {
177 fprintf(stderr, "Cannot retrieve services of BLE device, status: %d\n", status);
178 return -8;
179 }
180
181 // FINDING CHARACTERISTIC
182 // Again, even if you know beforehand the UUID of the characteristic you want, you
183 // still need to retrieve the DB, and then find your UUID in the local DB copy to
184 // interact with it
185 uuid_t characUuid = {
186 .uu =
187 {0xFF, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x00},
189 .type = ACEBT_UUID_TYPE_16,
190 };
191 struct aceBT_gattCharRec_t* charac = utilsFindCharRec(characUuid, 16);
192 if (charac == NULL) {
193 printf("Couldn't find the characteristic");
194 return -9;
195 }
196
197 // ENABLING NOTIFICATIONS ON CHARACTERISTIC
198 status = bleSetNotification(bt_session, conn_handle, charac->value, true);
199 if (status != ACE_STATUS_OK) {
200 fprintf(
201 stderr, "Cannot enable notifications on BLE device/characteristic, status: %d\n", status
202 );
203 return -10;
204 }
205
206 sleep(5);
207
208 // DISABLING NOTIFICATIONS ON CHARACTERISTIC
209 status = bleSetNotification(bt_session, conn_handle, charac->value, false);
210 if (status != ACE_STATUS_OK) {
211 fprintf(
212 stderr, "Cannot disable notifications on BLE device/characteristic, status: %d\n",
213 status
214 );
215 return -10;
216 }
217
218 sleep(2);
219
220 // Block reading and writing in a loop
221 for (int i = 0; i < 20; i++) {
222 // READING CHARACTERISTIC
223 status = bleReadCharacteristic(bt_session, conn_handle, charac->value);
224 if (status != ACE_STATUS_OK) {
225 fprintf(stderr, "Cannot read characteristic, status: %d\n", status);
226 return -11;
227 }
228 for (int j = 0; j < 10; j++) {
229 if (read_characteristic) break;
230 sleep(1);
231 }
232 if (!read_characteristic) {
233 fprintf(stderr, "Timed out waiting to read characteristic, status: %d\n", status);
234 return -11;
235 }
236 read_characteristic = false;
237 // Not sure if necessary but I always reset the blob after read/write
238 freeGattBlob(&charac->value);
239
240 // WRITING CHARACTERISTIC
241 // Values used in my characteristic as an example of how to write
242 uint8_t off[] = {'O', 'F', 'F'};
243 uint8_t on[] = {'O', 'N'};
244 uint8_t* states[] = {off, on};
245 size_t lengths[] = {sizeof(off), sizeof(on)};
246
247 // Now write the value to the characteristic struct
248 setGattBlobFromBytes(&charac->value, states[i % 2], lengths[i % 2]);
249
250 status = bleWriteCharacteristic(
251 bt_session, conn_handle, &charac->value, ACEBT_BLE_WRITE_TYPE_RESP_REQUIRED
252 );
253 if (status != ACE_STATUS_OK) {
254 fprintf(stderr, "Cannot write characteristic, status: %d\n", status);
255 return -12;
256 }
257 for (int j = 0; j < 10; j++) {
258 if (wrote_characteristic) break;
259 sleep(1);
260 }
261 if (!wrote_characteristic) {
262 fprintf(stderr, "Timed out waiting to write characteristic, status: %d\n", status);
263 return -12;
264 }
265 wrote_characteristic = false;
266 freeGattBlob(&charac->value);
267 }
268
269 sleep(2);
270
271 status = bleDisconnect(conn_handle);
272 printf("Disconnected from BLE status: %d\n", status);
273
274 status = bleDeregisterGattClient(bt_session);
275 printf("Deregistered GATT Client status: %d\n", status);
276
277 status = bleDeregister(bt_session);
278 printf("Deregistered BLE status: %d\n", status);
279
280 status = closeSession(bt_session);
281 printf("Closed session status: %d\n", status);
282
283 return 0;
284}
aceBT_bleGattClientCallbacks_t bleGattClientCallbacks_t
Callback struct of GATT Client Bluetooth operations.
aceBT_bdAddr_t bdAddr_t
Bluetooth address.
ace_status_t status_t
Bluetooth API status codes.
aceBT_bleGattBlobValue_t bleGattBlobValue_t
BLE blob value.
aceBT_bleGattsService_t bleGattsService_t
Structure for a GATT Server service.
aceBT_uuid_t uuid_t
Bluetooth UUID struct.
aceBT_bleConnHandle bleConnHandle
Connection handle for the lifetime of a Bluetooth connection.
#define BLE_FORMAT_BLOB
BLOB format.
aceBT_sessionHandle sessionHandle
Session handle for the lifetime of the Bluetooth application.
aceBT_bleGattCharacteristicsValue_t bleGattCharacteristicsValue_t
BLE GATT Characteristic.
status_t bleWriteCharacteristic(sessionHandle session_handle, bleConnHandle conn_handle, bleGattCharacteristicsValue_t *chars_value, responseType_t request_type)
Write a Characteristic from a BLE device.
Definition kindlebt.c:148
void kindlebt_set_log_level(log_level_t level)
Set the log level for log.c.
Definition kindlebt_log.c:9
status_t bleGetDatabase(bleConnHandle conn_handle, bleGattsService_t *p_gatt_service)
Retrieve all services of a remote GATT Server.
Definition kindlebt.c:105
status_t openSession(sessionType_t session_type, sessionHandle *session_handle)
Open a Bluetooth session.
Definition kindlebt.c:48
status_t bleRegisterGattClient(sessionHandle session_handle, bleGattClientCallbacks_t *callbacks)
Register as a BLE GATT Client.
Definition kindlebt.c:79
struct aceBT_gattCharRec_t * utilsFindCharRec(uuid_t uuid, uint8_t uuid_len)
Find a GATT Characteristic Record by UUID.
status_t bleRegister(sessionHandle session_handle)
Register as BLE client.
Definition kindlebt.c:54
status_t bleSetNotification(sessionHandle session_handle, bleConnHandle conn_handle, bleGattCharacteristicsValue_t chars_value, bool enable)
Set notifications on a Characteristic from a BLE device.
Definition kindlebt.c:162
status_t closeSession(sessionHandle session_handle)
Close a Bluetooth session.
Definition kindlebt.c:52
status_t bleDeregister(sessionHandle session_handle)
Deregister as BLE client.
Definition kindlebt.c:68
bool isBLESupported(void)
Is BLE supported by the hardware Bluetooth adapter.
Definition kindlebt.c:40
status_t bleReadCharacteristic(sessionHandle session_handle, bleConnHandle conn_handle, bleGattCharacteristicsValue_t chars_value)
Read a Characteristic from a BLE device.
Definition kindlebt.c:141
status_t bleDiscoverAllServices(sessionHandle session_handle, bleConnHandle conn_handle)
Discover all services of a remote GATT Server.
Definition kindlebt.c:95
status_t bleConnect(sessionHandle session_handle, bleConnHandle *conn_handle, bdAddr_t *p_device, bleConnParam_t conn_param, bleConnRole_t conn_role, bleConnPriority_t conn_priority)
Connect to a BLE device.
Definition kindlebt.c:123
status_t bleDisconnect(bleConnHandle conn_handle)
Disconnect from a BLE device.
Definition kindlebt.c:139
status_t bleDeregisterGattClient(sessionHandle session_handle)
Deregister as a BLE GATT Client.
Definition kindlebt.c:91
Bluetooth library for Kindles.