Mstdlib-1.24.0
m_io_ble.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2018 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_IO_BLE_H__
25#define __M_IO_BLE_H__
26
27#include <mstdlib/base/m_defs.h>
28#include <mstdlib/base/m_types.h>
29#include <mstdlib/base/m_list_str.h>
30#include <mstdlib/base/m_time.h>
31#include <mstdlib/io/m_io.h>
32#include <mstdlib/io/m_event.h>
33
34__BEGIN_DECLS
35
36/*! \addtogroup m_io_ble Bluetooth LE (Low Energy) IO functions
37 * \ingroup m_eventio_base
38 *
39 * Bluetooth LE (Low Energy) IO functions.
40 *
41 * Supported OS:
42 * - iOS
43 * - macOS
44 *
45 * \note iOS also supports an Apple proprietary system known as Made for iPhone/iPod/iPad (MFi).
46 * MFi uses ble but is handled differently. It's supposed though the m_io_mfi layer.
47 * MFi is also known as the External Accessory / EAAccessory protocol.
48 *
49 *
50 * ## Overview
51 *
52 * BLE was designed to keep energy consumption to a minimum and allow for seamless device
53 * access. Unlike Bluetooth classic, devices are not paired to the system. Typical use
54 * is to scan for available devices, inspect their services, and connect to a device that
55 * provides services the application wants to use. A good example is a heart rate monitor.
56 *
57 * A health app doesn't care which heart rate monitor is being used it only cares about
58 * getting heart rate data. Typically, the user will be presented with a list of suitable
59 * devices in case multiple devices are detected (for example, multiple people going on
60 * a bike ride together).
61 *
62 * Since there is no pairing the device must be found by scanning for available devices.
63 * All devices that have been found during a scan (excluding ones that have been pruned)
64 * will be listed as part of device enumeration. This means devices may no longer be present.
65 * Such as an iPhone being seen during scanning and later the owner of the phone leaving
66 * the room. There are no OS level events to notify that this has happened.
67 *
68 * When necessary, a scan can be initiated by trying to connect to a device.
69 * Opening a device requires specifying a device identifier or service UUID and if
70 * not found a scan will be started internally for either the duration of the timeout
71 * or until the device has been found. This can cause a delay between trying to open
72 * a device and receiving CONNECT or ERROR events.
73 *
74 * Device identifiers will vary between OS's. macOS for example assigns a device specific
75 * UUID to devices it sees. Android returns the device's MAC address. There is no way
76 * to read a device's MAC address on macOS. Identifiers are subject to change periodically.
77 * iOS will randomly change the device's MAC address every few hours to prevent tracking.
78 *
79 * BLE devices provide services and there can be multiple services. Services provide
80 * characteristics and there can be multiple characteristics per service. Both
81 * services and characteristics can be defined using standardized profiles. See
82 * the Bluetooth GATT specifications.
83 *
84 * Since there are multiple, potentially, read and write end points it is required
85 * to specify the service and characteristic UUIDs. A write event must have them
86 * specified using the M_io_meta_t and associated BLE meta functions. A read will
87 * fill a provided meta object with the service and characteristic the data came from.
88 * This means only the read and write meta functions can be use with BLE. The non-meta
89 * functions will return an error.
90 *
91 * Characteristics can have multiple properties.
92 * - Read
93 * - Notify
94 * - Indicate
95 * - Write
96 * - Write without response
97 *
98 * BLE by default is not a stream-based protocol like serial, HID, or Bluetooth classic.
99 * Characteristics with the read property can be requested to read data. This is an async
100 * request. M_io_ble facilitates this by using M_io_write_meta with a property indicator
101 * that specifies data is not being written but a request for read is being sent.
102 *
103 * Characteristics with the notify or indicate property can be subscribed to which will
104 * have them issue read events similar to a stream protocol. Reads will still require a meta
105 * object to specify which service and characteristic the data is from. Manual read requests
106 * may still be necessary. Notify and Indicate events are left to the device to initiate.
107 * The device may have internal rules which limit how often events are triggered. For example
108 * a heart rate monitor could notify every 2 seconds even though it's reading every 100 ms. A
109 * time service might send an event every second or it might send an event every minute.
110 *
111 * Characteristics won't receive read events by default. They need to be subscribed to first.
112 * Subscriptions will not service a disconnect or destroy of an io object. Also, not all characteristics
113 * support this property even if it supports read. Conversely some support notify/indicate but
114 * not read.
115 *
116 * Write will write data to the device and the OS will issue an event whether the write
117 * succeeded or failed. Mstdlib uses this to determine if there was a write error and will
118 * block subsequent writes (returns WOULDBLOCK) until an outstanding write has completed.
119 *
120 * Write without response is a blind write. No result is requested from the OS. The state
121 * of the write is not known after it is sent.
122 *
123 * When subscribing to a notification a write must take place with no data in order for the
124 * even to be registered. Typically, you'll want to register for events after connect (since
125 * a write is needed to initiate the registration. Once the even it is registered a
126 * `M_EVENT_TYPE_READ` event will be generated with the `M_IO_BLE_RTYPE_NOTIFY`
127 * meta type set. There will be no data present. This is an indicator
128 * registration was successful.
129 *
130 * Register on connect event:
131 *
132 * \code{.c}
133 * meta = M_io_meta_create();
134 * M_io_ble_meta_set_write_type(dio, meta, M_IO_BLE_WTYPE_REQNOTIFY);
135 * M_io_ble_meta_set_service(dio, meta, "1111");
136 * M_io_ble_meta_set_characteristic(dio, meta, "2222");
137 * M_io_write_meta(dio, NULL, 0, NULL, meta);
138 * M_io_meta_destroy(meta);
139 * \endcode
140 *
141 * Check the BLE read type if notification registration was complete.
142 * While not pictured, you should also check the service and characteristic
143 * that generated the event if multiple notification events are being registered
144 * to determine which this belongs to.
145 *
146 * \code{.c}
147 * meta = M_io_meta_create();
148 * if (M_io_read_meta(dio, msg, sizeof(msg), &len, meta) == M_IO_ERROR_SUCCESS) {
149 * if (M_io_ble_meta_get_read_type(dio, meta) == M_IO_BLE_RTYPE_NOTIFY) {
150 * M_printf("Notify enabled\n");
151 * }
152 * }
153 * M_io_meta_destroy(meta);
154 * \endcode
155 *
156 * ## macOS requirements
157 *
158 * BLE events are only delivered to the main run loop. This is a design decision by Apple.
159 * It is not possible to use an different run loop to receive events like can be done
160 * with classic Bluetooth or HID. BLE events are non-blocking so there shouldn't be any
161 * performance impact with the events being delivered. As little work as possible is
162 * performed during event processing to limit any impact of this design requirement.
163 *
164 * A C application will need to manually start the macOS main runloop, otherwise no events
165 * will be delivered and no BLE operations will work.
166 *
167 * ### Examples
168 *
169 * #### Application that scans for 30 seconds and enumerates all devices and their services that were seen.
170 *
171 * \code{.c}
172 * // Build:
173 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_enum.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
174 * //
175 * // Run:
176 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
177 *
178 * #include <mstdlib/mstdlib.h>
179 * #include <mstdlib/mstdlib_thread.h>
180 * #include <mstdlib/mstdlib_io.h>
181 * #include <mstdlib/io/m_io_ble.h>
182 *
183 * #include <CoreFoundation/CoreFoundation.h>
184 *
185 * M_event_t *el;
186 * CFRunLoopRef mrl = NULL;
187 *
188 * static void scan_done_cb(M_event_t *event, M_event_type_t type, M_io_t *io, void *cb_arg)
189 * {
190 * M_io_ble_enum_t *btenum;
191 * M_list_str_t *service_uuids;
192 * size_t len;
193 * size_t len2;
194 * size_t i;
195 * size_t j;
196 *
197 * (void)event;
198 * (void)type;
199 * (void)io;
200 * (void)cb_arg;
201 *
202 * btenum = M_io_ble_enum();
203 *
204 * len = M_io_ble_enum_count(btenum);
205 * M_printf("Num devs = %zu\n", len);
206 * for (i=0; i<len; i++) {
207 * M_printf("Device:\n");
208 * M_printf("\tName: %s\n", M_io_ble_enum_name(btenum, i));
209 * M_printf("\tIdentifier: %s\n", M_io_ble_enum_identifier(btenum, i));
210 * M_printf("\tLast Seen: %llu\n", M_io_ble_enum_last_seen(btenum, i));
211 * M_printf("\tSerivces:\n");
212 * service_uuids = M_io_ble_enum_service_uuids(btenum, i);
213 * len2 = M_list_str_len(service_uuids);
214 * for (j=0; j<len2; j++) {
215 * M_printf("\t\t: %s\n", M_list_str_at(service_uuids, j));
216 * }
217 * M_list_str_destroy(service_uuids);
218 * }
219 *
220 * M_io_ble_enum_destroy(btenum);
221 *
222 * if (mrl != NULL)
223 * CFRunLoopStop(mrl);
224 * }
225 *
226 * static void *run_el(void *arg)
227 * {
228 * (void)arg;
229 * M_event_loop(el, M_TIMEOUT_INF);
230 * return NULL;
231 * }
232 *
233 * int main(int argc, char **argv)
234 * {
235 * M_threadid_t el_thread;
236 * M_thread_attr_t *tattr;
237 *
238 * el = M_event_create(M_EVENT_FLAG_NONE);
239 *
240 * tattr = M_thread_attr_create();
241 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
242 * el_thread = M_thread_create(tattr, run_el, NULL);
243 * M_thread_attr_destroy(tattr);
244 *
245 * M_io_ble_scan(el, scan_done_cb, NULL, 30000);
246 *
247 * mrl = CFRunLoopGetCurrent();
248 * CFRunLoopRun();
249 *
250 * // 5 sec timeout.
251 * M_event_done_with_disconnect(el, 0, 5*1000);
252 * M_thread_join(el_thread, NULL);
253 *
254 * return 0;
255 * }
256 * \endcode
257 *
258 * #### Application that scans for 30 seconds and connects to a specified device which has been seen and cached (hopefully).
259 *
260 * \code{.c}
261 * // Build:
262 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_connect.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
263 * //
264 * // Run:
265 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
266 *
267 * #include <mstdlib/mstdlib.h>
268 * #include <mstdlib/mstdlib_thread.h>
269 * #include <mstdlib/mstdlib_io.h>
270 * #include <mstdlib/io/m_io_ble.h>
271 *
272 * #include <CoreFoundation/CoreFoundation.h>
273 *
274 * M_event_t *el;
275 * M_io_t *dio;
276 * CFRunLoopRef mrl = NULL;
277 *
278 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
279 * {
280 * (void)el;
281 * (void)io;
282 * (void)thunk;
283 *
284 * switch (etype) {
285 * case M_EVENT_TYPE_CONNECTED:
286 * M_printf("CONNECTED!!!\n");
287 * break;
288 * case M_EVENT_TYPE_DISCONNECTED:
289 * M_printf("DISCONNECTED!!!\n");
290 * M_io_destroy(dio);
291 * if (mrl != NULL)
292 * CFRunLoopStop(mrl);
293 * break;
294 * case M_EVENT_TYPE_READ:
295 * case M_EVENT_TYPE_WRITE:
296 * case M_EVENT_TYPE_ACCEPT:
297 * break;
298 * case M_EVENT_TYPE_ERROR:
299 * M_io_destroy(dio);
300 * if (mrl != NULL)
301 * CFRunLoopStop(mrl);
302 * break;
303 * case M_EVENT_TYPE_OTHER:
304 * break;
305 * }
306 * }
307 *
308 * static void scan_done_cb(M_event_t *event, M_event_type_t type, M_io_t *io, void *cb_arg)
309 * {
310 * M_io_ble_enum_t *btenum;
311 * size_t len;
312 * size_t i;
313 *
314 * (void)event;
315 * (void)type;
316 * (void)io;
317 * (void)cb_arg;
318 *
319 * // XXX: Set the id to the device you want to connect to.
320 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
321 * M_event_add(el, dio, events, NULL);
322 *
323 * M_printf("SCAN DONE\n");
324 * }
325 *
326 * static void *run_el(void *arg)
327 * {
328 * (void)arg;
329 * M_event_loop(el, M_TIMEOUT_INF);
330 * return NULL;
331 * }
332 *
333 * int main(int argc, char **argv)
334 * {
335 * M_threadid_t el_thread;
336 * M_thread_attr_t *tattr;
337 *
338 * el = M_event_create(M_EVENT_FLAG_NONE);
339 *
340 * tattr = M_thread_attr_create();
341 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
342 * el_thread = M_thread_create(tattr, run_el, NULL);
343 * M_thread_attr_destroy(tattr);
344 *
345 * M_io_ble_scan(el, scan_done_cb, NULL, 5000);
346 *
347 * mrl = CFRunLoopGetCurrent();
348 * CFRunLoopRun();
349 *
350 * M_event_done_with_disconnect(el, 0, 5*1000);
351 * M_thread_join(el_thread, NULL);
352 *
353 * return 0;
354 * }
355 * \endcode
356 *
357 * #### Application that implicitly scans and connects to a specified device which has not been seen and cached.
358 *
359 * \code{.c}
360 * // Build:
361 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_connect_noscan.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
362 * //
363 * // Run:
364 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
365 *
366 * #include <mstdlib/mstdlib.h>
367 * #include <mstdlib/mstdlib_thread.h>
368 * #include <mstdlib/mstdlib_io.h>
369 * #include <mstdlib/io/m_io_ble.h>
370 *
371 * #include <CoreFoundation/CoreFoundation.h>
372 *
373 * M_event_t *el;
374 * M_io_t *dio;
375 * CFRunLoopRef mrl = NULL;
376 *
377 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
378 * {
379 * (void)el;
380 * (void)io;
381 * (void)thunk;
382 *
383 * switch (etype) {
384 * case M_EVENT_TYPE_CONNECTED:
385 * M_printf("CONNECTED!!!\n");
386 * M_io_disconnect(dio);
387 * break;
388 * case M_EVENT_TYPE_DISCONNECTED:
389 * M_printf("DISCONNECTED!!!\n");
390 * case M_EVENT_TYPE_READ:
391 * case M_EVENT_TYPE_WRITE:
392 * case M_EVENT_TYPE_ACCEPT:
393 * case M_EVENT_TYPE_ERROR:
394 * M_io_destroy(dio);
395 * if (mrl != NULL)
396 * CFRunLoopStop(mrl);
397 * break;
398 * case M_EVENT_TYPE_OTHER:
399 * break;
400 * }
401 * }
402 *
403 * static void *run_el(void *arg)
404 * {
405 * (void)arg;
406 * M_event_loop(el, M_TIMEOUT_INF);
407 * return NULL;
408 * }
409 *
410 * int main(int argc, char **argv)
411 * {
412 * M_threadid_t el_thread;
413 * M_thread_attr_t *tattr;
414 *
415 * el = M_event_create(M_EVENT_FLAG_NONE);
416 *
417 * tattr = M_thread_attr_create();
418 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
419 * el_thread = M_thread_create(tattr, run_el, NULL);
420 * M_thread_attr_destroy(tattr);
421 *
422 * // XXX: Set the id to the device you want to connect to.
423 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
424 * M_event_add(el, dio, events, NULL);
425 *
426 * mrl = CFRunLoopGetCurrent();
427 * CFRunLoopRun();
428 *
429 * M_event_done_with_disconnect(el, 0, 5*1000);
430 * M_thread_join(el_thread, NULL);
431 *
432 * return 0;
433 * }
434 * \endcode
435 *
436 *
437 * #### Application that enumerates all devices, connects to them and inspects their services, characteristics and characteristic properties
438 *
439 * \code{.c}
440 * // Build:
441 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_list.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
442 * //
443 * // Run:
444 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
445 *
446 * #include <mstdlib/mstdlib.h>
447 * #include <mstdlib/mstdlib_thread.h>
448 * #include <mstdlib/mstdlib_io.h>
449 * #include <mstdlib/io/m_io_ble.h>
450 *
451 * #include <CoreFoundation/CoreFoundation.h>
452 *
453 * M_event_t *el;
454 * CFRunLoopRef mrl = NULL;
455 * size_t cnt = 0;
456 *
457 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
458 * {
459 * char *temp;
460 * const char *service_uuid;
461 * const char *characteristic_uuid;
462 * M_list_str_t *services;
463 * M_list_str_t *characteristics;
464 * M_io_ble_property_t props;
465 * size_t len;
466 * size_t len2;
467 * size_t i;
468 * size_t j;
469 *
470 * (void)el;
471 * (void)thunk;
472 *
473 * switch (etype) {
474 * case M_EVENT_TYPE_CONNECTED:
475 * M_printf("Device:\n");
476 *
477 * temp = M_io_ble_get_identifier(io);
478 * M_printf("\tIdentifier: %s\n", temp);
479 * M_free(temp);
480 *
481 * temp = M_io_ble_get_name(io);
482 * M_printf("\tName: %s\n", temp);
483 * M_free(temp);
484 *
485 * services = M_io_ble_get_services(io);
486 * len = M_list_str_len(services);
487 * for (i=0; i<len; i++) {
488 * service_uuid = M_list_str_at(services, i);
489 * M_printf("\t\tService = %s:\n", service_uuid);
490 *
491 * characteristics = M_io_ble_get_service_characteristics(io, M_list_str_at(services, i));
492 * len2 = M_list_str_len(characteristics);
493 * for (j=0; j<len2; j++) {
494 * characteristic_uuid = M_list_str_at(characteristics, j);
495 * M_printf("\t\t\tCharacteristic = %s\n", characteristic_uuid);
496 *
497 * props = M_io_ble_get_characteristic_properties(io, service_uuid, characteristic_uuid);
498 * if (props == M_IO_BLE_PROPERTY_NONE) {
499 * M_printf("\t\t\t\tProperty = NONE\n");
500 * }
501 * if (props & M_IO_BLE_PROPERTY_READ) {
502 * M_printf("\t\t\t\tProperty = READ\n");
503 * }
504 * if (props & M_IO_BLE_PROPERTY_WRITE) {
505 * M_printf("\t\t\t\tProperty = WRITE\n");
506 * }
507 * if (props & M_IO_BLE_PROPERTY_WRITENORESP) {
508 * M_printf("\t\t\t\tProperty = WRITE NO RESPONSE\n");
509 * }
510 * if (props & M_IO_BLE_PROPERTY_NOTIFY) {
511 * M_printf("\t\t\t\tProperty = NOTIFY\n");
512 * }
513 * }
514 * M_list_str_destroy(characteristics);
515 * }
516 * M_list_str_destroy(services);
517 *
518 * M_io_disconnect(io);
519 * break;
520 * case M_EVENT_TYPE_DISCONNECTED:
521 * case M_EVENT_TYPE_READ:
522 * case M_EVENT_TYPE_WRITE:
523 * case M_EVENT_TYPE_ACCEPT:
524 * case M_EVENT_TYPE_ERROR:
525 * M_io_destroy(io);
526 * cnt--;
527 * if (cnt == 0 && mrl != NULL)
528 * CFRunLoopStop(mrl);
529 * break;
530 * case M_EVENT_TYPE_OTHER:
531 * break;
532 * }
533 * }
534 *
535 * static void scan_done_cb(M_event_t *event, M_event_type_t type, M_io_t *io, void *cb_arg)
536 * {
537 * M_io_ble_enum_t *btenum;
538 * M_io_error_t io_err;
539 * size_t len;
540 * size_t i;
541 *
542 * (void)event;
543 * (void)type;
544 * (void)io;
545 * (void)cb_arg;
546 *
547 * btenum = M_io_ble_enum();
548 *
549 * len = M_io_ble_enum_count(btenum);
550 * for (i=0; i<len; i++) {
551 * M_io_t *dio = NULL;
552 *
553 * io_err = M_io_ble_create(&dio, M_io_ble_enum_identifier(btenum, i), 5000);
554 * if (io_err == M_IO_ERROR_SUCCESS) {
555 * M_event_add(el, dio, events, NULL);
556 * cnt++;
557 * }
558 * }
559 *
560 * if (cnt == 0 && mrl != NULL)
561 * CFRunLoopStop(mrl);
562 *
563 * M_io_ble_enum_destroy(btenum);
564 * }
565 *
566 * static void *run_el(void *arg)
567 * {
568 * (void)arg;
569 * M_event_loop(el, M_TIMEOUT_INF);
570 * return NULL;
571 * }
572 *
573 * int main(int argc, char **argv)
574 * {
575 * M_threadid_t el_thread;
576 * M_thread_attr_t *tattr;
577 *
578 * el = M_event_create(M_EVENT_FLAG_NONE);
579 *
580 * tattr = M_thread_attr_create();
581 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
582 * el_thread = M_thread_create(tattr, run_el, NULL);
583 * M_thread_attr_destroy(tattr);
584 *
585 * M_io_ble_scan(el, scan_done_cb, NULL, 15000);
586 * mrl = CFRunLoopGetCurrent();
587 * CFRunLoopRun();
588 *
589 * M_event_done_with_disconnect(el, 0, 5*1000);
590 * M_thread_join(el_thread, NULL);
591 *
592 * return 0;
593 * }
594 * \endcode
595 *
596 *
597 * #### Application that lists services and their characteristics for a specific device
598 *
599 * \code{.c}
600 * // Build:
601 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_interrogate.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
602 * //
603 * // Run:
604 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
605 *
606 * #include <mstdlib/mstdlib.h>
607 * #include <mstdlib/mstdlib_thread.h>
608 * #include <mstdlib/mstdlib_io.h>
609 * #include <mstdlib/io/m_io_ble.h>
610 *
611 * #include <CoreFoundation/CoreFoundation.h>
612 *
613 * M_event_t *el;
614 * M_io_t *dio;
615 * CFRunLoopRef mrl = NULL;
616 *
617 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
618 * {
619 * M_list_str_t *services;
620 * M_list_str_t *characteristics;
621 * size_t len;
622 * size_t len2;
623 * size_t i;
624 * size_t j;
625 *
626 * (void)el;
627 * (void)io;
628 * (void)thunk;
629 *
630 * switch (etype) {
631 * case M_EVENT_TYPE_CONNECTED:
632 * M_printf("CONNECTED!!!\n");
633 *
634 * services = M_io_ble_get_services(dio);
635 * len = M_list_str_len(services);
636 * for (i=0; i<len; i++) {
637 * M_printf("service = %s:\n", M_list_str_at(services, i));
638 * characteristics = M_io_ble_get_service_characteristics(dio, M_list_str_at(services, i));
639 * len2 = M_list_str_len(characteristics);
640 * for (j=0; j<len2; j++) {
641 * M_printf("\t%s\n", M_list_str_at(characteristics, j));
642 * }
643 * M_list_str_destroy(characteristics);
644 * }
645 * M_list_str_destroy(services);
646 *
647 * M_io_disconnect(dio);
648 * break;
649 * case M_EVENT_TYPE_DISCONNECTED:
650 * M_printf("DISCONNECTED!!!\n");
651 * case M_EVENT_TYPE_READ:
652 * case M_EVENT_TYPE_WRITE:
653 * case M_EVENT_TYPE_ACCEPT:
654 * case M_EVENT_TYPE_ERROR:
655 * M_io_destroy(dio);
656 * if (mrl != NULL)
657 * CFRunLoopStop(mrl);
658 * break;
659 * case M_EVENT_TYPE_OTHER:
660 * break;
661 * }
662 * }
663 *
664 * static void *run_el(void *arg)
665 * {
666 * (void)arg;
667 * M_event_loop(el, M_TIMEOUT_INF);
668 * return NULL;
669 * }
670 *
671 * int main(int argc, char **argv)
672 * {
673 * M_threadid_t el_thread;
674 * M_thread_attr_t *tattr;
675 *
676 * el = M_event_create(M_EVENT_FLAG_NONE);
677 *
678 * tattr = M_thread_attr_create();
679 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
680 * el_thread = M_thread_create(tattr, run_el, NULL);
681 * M_thread_attr_destroy(tattr);
682 *
683 * // XXX: Set the id to the device you want to connect to.
684 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
685 * M_event_add(el, dio, events, NULL);
686 *
687 * mrl = CFRunLoopGetCurrent();
688 * CFRunLoopRun();
689 *
690 * M_event_done_with_disconnect(el, 0, 5*1000);
691 * M_thread_join(el_thread, NULL);
692 *
693 * return 0;
694 * }
695 * \endcode
696 *
697 * #### Application that reads by polling the device for a read using a write
698 *
699 * \code{.c}
700 * // Build:
701 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_readp.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
702 * //
703 * // Run:
704 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
705 *
706 * #include <mstdlib/mstdlib.h>
707 * #include <mstdlib/mstdlib_thread.h>
708 * #include <mstdlib/mstdlib_io.h>
709 * #include <mstdlib/io/m_io_ble.h>
710 *
711 * #include <CoreFoundation/CoreFoundation.h>
712 *
713 * M_event_t *el;
714 * M_io_t *dio;
715 * M_io_meta_t *wmeta;
716 * CFRunLoopRef mrl = NULL;
717 *
718 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
719 * {
720 * M_int64 rssi = M_INT64_MIN;
721 * M_io_meta_t *rmeta = NULL;
722 * const char *service_uuid;
723 * const char *characteristic_uuid;
724 * char msg[256];
725 * size_t len;
726 *
727 * (void)el;
728 * (void)io;
729 * (void)thunk;
730 *
731 * switch (etype) {
732 * case M_EVENT_TYPE_CONNECTED:
733 * M_printf("CONNECTED!!!\n");
734 * M_io_write_meta(dio, NULL, 0, NULL, wmeta);
735 * break;
736 * case M_EVENT_TYPE_READ:
737 * rmeta = M_io_meta_create();
738 * M_io_read_meta(dio, msg, sizeof(msg)-1, &len, rmeta);
739 * msg[len] = '\0';
740 * service_uuid = M_io_ble_meta_get_service(dio, rmeta);
741 * characteristic_uuid = M_io_ble_meta_get_characteristic(dio, rmeta);
742 *
743 * M_printf("%s - %s: %s\n", service_uuid, characteristic_uuid, msg);
744 *
745 * M_io_meta_destroy(rmeta);
746 *
747 * M_thread_sleep(100000);
748 * M_io_write_meta(dio, NULL, 0, NULL, wmeta);
749 * break;
750 * case M_EVENT_TYPE_WRITE:
751 * break;
752 * case M_EVENT_TYPE_ERROR:
753 * M_io_get_error_string(dio, msg, sizeof(msg));
754 * M_printf("ERROR: %s\n", msg);
755 * case M_EVENT_TYPE_DISCONNECTED:
756 * if (etype == M_EVENT_TYPE_DISCONNECTED)
757 * M_printf("DISCONNECTED!!!\n");
758 * M_io_destroy(dio);
759 * if (mrl != NULL)
760 * CFRunLoopStop(mrl);
761 * break;
762 * case M_EVENT_TYPE_ACCEPT:
763 * case M_EVENT_TYPE_OTHER:
764 * break;
765 * }
766 * }
767 *
768 * static void *run_el(void *arg)
769 * {
770 * (void)arg;
771 * M_event_loop(el, M_TIMEOUT_INF);
772 * return NULL;
773 * }
774 *
775 * int main(int argc, char **argv)
776 * {
777 * M_threadid_t el_thread;
778 * M_thread_attr_t *tattr;
779 *
780 * el = M_event_create(M_EVENT_FLAG_NONE);
781 *
782 * tattr = M_thread_attr_create();
783 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
784 * el_thread = M_thread_create(tattr, run_el, NULL);
785 * M_thread_attr_destroy(tattr);
786 *
787 * // XXX: Set the id to the device you want to connect to.
788 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
789 * wmeta = M_io_meta_create();
790 * M_io_ble_meta_set_write_type(dio, wmeta, M_IO_BLE_WTYPE_REQVAL);
791 * M_io_ble_meta_set_service(dio, wmeta, "1111");
792 * M_io_ble_meta_set_characteristic(dio, wmeta, "2222");
793 * M_event_add(el, dio, events, NULL);
794 *
795 * mrl = CFRunLoopGetCurrent();
796 * CFRunLoopRun();
797 *
798 * M_event_done_with_disconnect(el, 0, 5*1000);
799 * M_thread_join(el_thread, NULL);
800 *
801 * M_io_meta_destroy(wmeta);
802 *
803 * return 0;
804 * }
805 * \endcode
806 *
807 * #### Application that reads by requesting notification when value changes
808 *
809 * \code{.c}
810 * // Build:
811 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_readn.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
812 * //
813 * // Run:
814 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
815 *
816 * #include <mstdlib/mstdlib.h>
817 * #include <mstdlib/mstdlib_thread.h>
818 * #include <mstdlib/mstdlib_io.h>
819 * #include <mstdlib/io/m_io_ble.h>
820 *
821 * #include <CoreFoundation/CoreFoundation.h>
822 *
823 * M_event_t *el;
824 * M_io_t *dio;
825 * CFRunLoopRef mrl = NULL;
826 *
827 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
828 * {
829 * M_int64 rssi = M_INT64_MIN;
830 * M_io_meta_t *meta = NULL;
831 * const char *service_uuid;
832 * const char *characteristic_uuid;
833 * char msg[256];
834 * size_t len;
835 *
836 * (void)el;
837 * (void)io;
838 * (void)thunk;
839 *
840 * switch (etype) {
841 * case M_EVENT_TYPE_CONNECTED:
842 * M_printf("CONNECTED!!!\n");
843 *
844 * // XXX: Set notify service and characteristic.
845 * meta = M_io_meta_create();
846 * M_io_ble_meta_set_write_type(dio, meta, M_IO_BLE_WTYPE_REQNOTIFY);
847 * M_io_ble_meta_set_service(dio, meta, "1111");
848 * M_io_ble_meta_set_characteristic(dio, meta, "2222");
849 * M_io_write_meta(dio, NULL, 0, NULL, meta);
850 * M_io_meta_destroy(meta);
851 * break;
852 * case M_EVENT_TYPE_READ:
853 * meta = M_io_meta_create();
854 *
855 * meta = M_io_meta_create();
856 * if (M_io_read_meta(dio, msg, sizeof(msg), &len, meta) != M_IO_ERROR_SUCCESS) {
857 * M_io_meta_destroy(meta);
858 * break;
859 * }
860 * if (M_io_ble_meta_get_read_type(dio, meta) == M_IO_BLE_RTYPE_NOTIFY) {
861 * M_printf("Notify enabled\n");
862 * M_io_meta_destroy(meta);
863 * break;
864 * } else if (M_io_ble_meta_get_read_type(dio, meta) != M_IO_BLE_RTYPE_READ) {
865 * M_io_meta_destroy(meta);
866 * break;
867 * }
868 *
869 * msg[len] = '\0';
870 * service_uuid = M_io_ble_meta_get_service(dio, meta);
871 * characteristic_uuid = M_io_ble_meta_get_characteristic(dio, meta);
872 *
873 * M_printf("%s - %s: %s\n", service_uuid, characteristic_uuid, msg);
874 *
875 * M_io_meta_destroy(meta);
876 * break;
877 * case M_EVENT_TYPE_WRITE:
878 * break;
879 * case M_EVENT_TYPE_ERROR:
880 * M_io_get_error_string(dio, msg, sizeof(msg));
881 * M_printf("ERROR: %s\n", msg);
882 * case M_EVENT_TYPE_DISCONNECTED:
883 * if (etype == M_EVENT_TYPE_DISCONNECTED)
884 * M_printf("DISCONNECTED!!!\n");
885 * M_io_destroy(dio);
886 * if (mrl != NULL)
887 * CFRunLoopStop(mrl);
888 * break;
889 * case M_EVENT_TYPE_ACCEPT:
890 * case M_EVENT_TYPE_OTHER:
891 * break;
892 * }
893 * }
894 *
895 * static void *run_el(void *arg)
896 * {
897 * (void)arg;
898 * M_event_loop(el, M_TIMEOUT_INF);
899 * return NULL;
900 * }
901 *
902 * int main(int argc, char **argv)
903 * {
904 * M_threadid_t el_thread;
905 * M_thread_attr_t *tattr;
906 *
907 * el = M_event_create(M_EVENT_FLAG_NONE);
908 *
909 * tattr = M_thread_attr_create();
910 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
911 * el_thread = M_thread_create(tattr, run_el, NULL);
912 * M_thread_attr_destroy(tattr);
913 *
914 * // XXX: Set the id to the device you want to connect to.
915 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
916 * M_event_add(el, dio, events, NULL);
917 *
918 * mrl = CFRunLoopGetCurrent();
919 * CFRunLoopRun();
920 *
921 * M_event_done_with_disconnect(el, 0, 5*1000);
922 * M_thread_join(el_thread, NULL);
923 *
924 * return 0;
925 * }
926 * \endcode
927 *
928 * #### Application that uses writes to request current RSSI value.
929 *
930 * \code{.c}
931 * // Build:
932 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_rssi.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
933 * //
934 * // Run:
935 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
936 *
937 * #include <mstdlib/mstdlib.h>
938 * #include <mstdlib/mstdlib_thread.h>
939 * #include <mstdlib/mstdlib_io.h>
940 * #include <mstdlib/io/m_io_ble.h>
941 *
942 * #include <CoreFoundation/CoreFoundation.h>
943 *
944 * M_event_t *el;
945 * M_io_t *dio;
946 * M_io_meta_t *wmeta;
947 * CFRunLoopRef mrl = NULL;
948 *
949 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
950 * {
951 * M_int64 rssi = M_INT64_MIN;
952 * M_io_meta_t *rmeta = NULL;
953 * char msg[256];
954 *
955 * (void)el;
956 * (void)io;
957 * (void)thunk;
958 *
959 * switch (etype) {
960 * case M_EVENT_TYPE_CONNECTED:
961 * M_printf("CONNECTED!!!\n");
962 * M_io_write_meta(dio, NULL, 0, NULL, wmeta);
963 * break;
964 * case M_EVENT_TYPE_READ:
965 * rmeta = M_io_meta_create();
966 * M_io_read_meta(dio, NULL, 0, NULL, rmeta);
967 * M_io_ble_meta_get_rssi(dio, rmeta, &rssi);
968 * M_io_meta_destroy(rmeta);
969 *
970 * M_printf("RSSI = %lld\n", rssi);
971 *
972 * M_thread_sleep(100000);
973 * M_io_write_meta(dio, NULL, 0, NULL, wmeta);
974 * break;
975 * case M_EVENT_TYPE_WRITE:
976 * break;
977 * case M_EVENT_TYPE_ERROR:
978 * M_io_get_error_string(dio, msg, sizeof(msg));
979 * M_printf("ERROR: %s\n", msg);
980 * case M_EVENT_TYPE_DISCONNECTED:
981 * if (etype == M_EVENT_TYPE_DISCONNECTED)
982 * M_printf("DISCONNECTED!!!\n");
983 * M_io_destroy(dio);
984 * if (mrl != NULL)
985 * CFRunLoopStop(mrl);
986 * break;
987 * case M_EVENT_TYPE_ACCEPT:
988 * case M_EVENT_TYPE_OTHER:
989 * break;
990 * }
991 * }
992 *
993 * static void *run_el(void *arg)
994 * {
995 * (void)arg;
996 * M_event_loop(el, M_TIMEOUT_INF);
997 * return NULL;
998 * }
999 *
1000 * int main(int argc, char **argv)
1001 * {
1002 * M_threadid_t el_thread;
1003 * M_thread_attr_t *tattr;
1004 *
1005 * el = M_event_create(M_EVENT_FLAG_NONE);
1006 *
1007 * tattr = M_thread_attr_create();
1008 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
1009 * el_thread = M_thread_create(tattr, run_el, NULL);
1010 * M_thread_attr_destroy(tattr);
1011 *
1012 * // XXX: Set the id to the device you want to connect to.
1013 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
1014 * wmeta = M_io_meta_create();
1015 * M_io_ble_meta_set_write_type(dio, wmeta, M_IO_BLE_WTYPE_REQRSSI);
1016 * M_event_add(el, dio, events, NULL);
1017 *
1018 * mrl = CFRunLoopGetCurrent();
1019 * CFRunLoopRun();
1020 *
1021 * M_event_done_with_disconnect(el, 0, 5*1000);
1022 * M_thread_join(el_thread, NULL);
1023 *
1024 * M_io_meta_destroy(wmeta);
1025 *
1026 * return 0;
1027 * }
1028 * \endcode
1029 *
1030 * #### Application that writes
1031 *
1032 * \code{.c}
1033 * // Build:
1034 * // clang -g -fobjc-arc -framework CoreFoundation test_ble_write.c -I ../../include/ -L ../../build/lib/ -l mstdlib_io -l mstdlib_thread -l mstdlib
1035 * //
1036 * // Run:
1037 * // DYLD_LIBRARY_PATH="../../build/lib/" ./a.out
1038 *
1039 * #include <mstdlib/mstdlib.h>
1040 * #include <mstdlib/mstdlib_thread.h>
1041 * #include <mstdlib/mstdlib_io.h>
1042 * #include <mstdlib/io/m_io_ble.h>
1043 *
1044 * #include <CoreFoundation/CoreFoundation.h>
1045 *
1046 * M_event_t *el;
1047 * M_io_t *dio;
1048 * M_io_meta_t *meta;
1049 * CFRunLoopRef mrl = NULL;
1050 *
1051 * void events(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
1052 * {
1053 * size_t len = 0;
1054 * static size_t num = 1;
1055 * char msg[256];
1056 * M_io_error_t ret;
1057 *
1058 * (void)el;
1059 * (void)io;
1060 * (void)thunk;
1061 *
1062 * M_snprintf(msg, sizeof(msg), "%zu", num++);
1063 *
1064 * switch (etype) {
1065 * case M_EVENT_TYPE_CONNECTED:
1066 * M_printf("CONNECTED!!!\n");
1067 * M_io_write_meta(dio, (const unsigned char *)msg, M_str_len(msg), &len, meta);
1068 * break;
1069 * case M_EVENT_TYPE_READ:
1070 * break;
1071 * case M_EVENT_TYPE_WRITE:
1072 * M_printf("WRITE\n");
1073 * M_io_write_meta(dio, (const unsigned char *)msg, M_str_len(msg), &len, meta);
1074 * M_thread_sleep(100000);
1075 * break;
1076 * case M_EVENT_TYPE_ERROR:
1077 * M_io_get_error_string(dio, msg, sizeof(msg));
1078 * M_printf("ERROR: %s\n", msg);
1079 * case M_EVENT_TYPE_DISCONNECTED:
1080 * if (etype == M_EVENT_TYPE_DISCONNECTED)
1081 * M_printf("DISCONNECTED!!!\n");
1082 * M_io_destroy(dio);
1083 * if (mrl != NULL)
1084 * CFRunLoopStop(mrl);
1085 * break;
1086 * case M_EVENT_TYPE_ACCEPT:
1087 * case M_EVENT_TYPE_OTHER:
1088 * break;
1089 * }
1090 * }
1091 *
1092 * static void *run_el(void *arg)
1093 * {
1094 * (void)arg;
1095 * M_event_loop(el, M_TIMEOUT_INF);
1096 * return NULL;
1097 * }
1098 *
1099 * int main(int argc, char **argv)
1100 * {
1101 * M_threadid_t el_thread;
1102 * M_thread_attr_t *tattr;
1103 *
1104 * el = M_event_create(M_EVENT_FLAG_NONE);
1105 *
1106 * tattr = M_thread_attr_create();
1107 * M_thread_attr_set_create_joinable(tattr, M_TRUE);
1108 * el_thread = M_thread_create(tattr, run_el, NULL);
1109 * M_thread_attr_destroy(tattr);
1110 *
1111 * // XXX: Set the id to the device you want to connect to.
1112 * M_io_ble_create(&dio, "92BD9AC6-3BC8-4B24-8BF8-AE583AFE3ED4", 5000);
1113 * meta = M_io_meta_create();
1114 * M_io_ble_meta_set_service(dio, meta, "1111");
1115 * M_io_ble_meta_set_characteristic(dio, meta, "2222");
1116 * M_event_add(el, dio, events, NULL);
1117 *
1118 * mrl = CFRunLoopGetCurrent();
1119 * CFRunLoopRun();
1120 *
1121 * M_event_done_with_disconnect(el, 0, 5*1000);
1122 * M_thread_join(el_thread, NULL);
1123 *
1124 * M_io_meta_destroy(meta);
1125 *
1126 * return 0;
1127 * }
1128 * \endcode
1129 * @{
1130 */
1131
1132/*! Meta property types used by M_io_write_meta.
1133 *
1134 * Specifies how the write should function. */
1135typedef enum {
1136 M_IO_BLE_WTYPE_WRITE = 0, /*!< Normal write. Waits for confirmation data was
1137 written before writes can take place again. */
1138 M_IO_BLE_WTYPE_WRITENORESP, /*!< Write without confirmation response. Blind write. */
1139 M_IO_BLE_WTYPE_REQVAL, /*!< Request value for service and characteristic. Not
1140 an actual write but a pseudo write to poll for a
1141 read event. */
1142 M_IO_BLE_WTYPE_REQRSSI, /*!< Request RSSI value. */
1143 M_IO_BLE_WTYPE_REQNOTIFY /*!< Request to change notify state. */
1145
1146
1147/*! Meta types used by M_io_read_meta.
1148 *
1149 * Specifies what type of read is being returned.
1150 */
1151typedef enum {
1152 M_IO_BLE_RTYPE_READ = 0, /*!< Regular read of data from service and characteristic. Read will return data. */
1153 M_IO_BLE_RTYPE_RSSI, /*!< RSSI data read. Use M_io_ble_meta_get_rssi. Read will not return data. All information should be access through meta methods. */
1154 M_IO_BLE_RTYPE_NOTIFY /*!< Notify state changed. There will be no data. This only indicates something happened with a notification end point subscription. Read will not return data because this is an indicator. */
1156
1157
1158/*! Characteristic properties.
1159 *
1160 * This is the subset of properties currently supported and used
1161 * for interaction with the device. Other types of properties, such
1162 * as extended, properties or ones indicating encryption requirements
1163 * are not currently included.
1164 */
1165typedef enum {
1172
1173
1174struct M_io_ble_enum;
1176
1177
1178/*! Start a BLE scan.
1179 *
1180 * A scan needs to take place for nearby devices to be found. Once found they will
1181 * appear in an enumeration.
1182 *
1183 * Opening a known device does not require explicitly scanning. Scanning
1184 * will happen implicitly if the device has not been seen before.
1185 *
1186 * \warning On macOS the callback will never be called if the main event loop is not running!
1187 *
1188 * \param[out] event Event handle to receive scan events.
1189 * \param[in] callback User-specified callback to call when the scan finishes
1190 * \param[in] cb_data Optional. User-specified data supplied to user-specified callback when
1191 * executed.
1192 * \param[in] timeout_ms How long the scan should run before stopping.
1193 * 0 will default to 1 minute. Scanning for devices
1194 * can take a long time. During testing of a simple
1195 * pedometer times upwards of 50 seconds were seen
1196 * before the device was detected while sitting
1197 * 6 inches away. A maximum of 5 minutes is allowed.
1198 * Any amount larger will be reduced to the max.
1199 *
1200 * \return M_TRUE if the scan was started and the callback will be called.
1201 * Otherwise M_FALSE, the callback will not be called.
1202 */
1203M_API M_bool M_io_ble_scan(M_event_t *event, M_event_callback_t callback, void *cb_data, M_uint64 timeout_ms);
1204
1205
1206/*! Create a ble enumeration object.
1207 *
1208 * You must call M_io_ble_scan to populate the enumeration.
1209 * Failing to do so will result in an empty enumeration.
1210 *
1211 * Use to determine what ble devices are connected and
1212 * what services are being offered. This is a
1213 * list of associated devices not necessarily what's actively connected.
1214 *
1215 * The enumeration is based on available services. Meaning a device may
1216 * be listed multiple times if it exposes multiple services.
1217 *
1218 * \return Bluetooth enumeration object.
1219 */
1221
1222
1223/*! Destroy a ble enumeration object.
1224 *
1225 * \param[in] btenum Bluetooth enumeration object.
1226 */
1228
1229
1230/*! Number of ble objects in the enumeration.
1231 *
1232 * \param[in] btenum Bluetooth enumeration object.
1233 *
1234 * \return Count of ble devices.
1235 */
1236M_API size_t M_io_ble_enum_count(const M_io_ble_enum_t *btenum);
1237
1238
1239/*! UUID of ble device.
1240 *
1241 * \param[in] btenum Bluetooth enumeration object.
1242 * \param[in] idx Index in ble enumeration.
1243 *
1244 * \return String.
1245 */
1246M_API const char *M_io_ble_enum_identifier(const M_io_ble_enum_t *btenum, size_t idx);
1247
1248
1249/*! Name of ble device as reported by the device.
1250 *
1251 * \param[in] btenum Bluetooth enumeration object.
1252 * \param[in] idx Index in ble enumeration.
1253 *
1254 * \return String.
1255 */
1256M_API const char *M_io_ble_enum_name(const M_io_ble_enum_t *btenum, size_t idx);
1257
1258
1259/*! UUIDs of services reported by device.
1260 *
1261 * This could be empty if the device has not been opened. Some devices
1262 * do not advertise this unless they are opened and interrogated.
1263 *
1264 * \param[in] btenum Bluetooth enumeration object.
1265 * \param[in] idx Index in ble enumeration.
1266 *
1267 * \return String list of UUIDs.
1268 */
1270
1271
1272/*! Last time the device was seen.
1273 *
1274 * Run a scan to ensure this is up to date. Opening a device will also
1275 * update the last seen time.
1276 *
1277 * \param[in] btenum Bluetooth enumeration object.
1278 * \param[in] idx Index in ble enumeration.
1279 *
1280 * \return time.
1281 */
1282M_API M_time_t M_io_ble_enum_last_seen(const M_io_ble_enum_t *btenum, size_t idx);
1283
1284
1285/*! Create a ble connection.
1286 *
1287 * \param[out] io_out io object for communication.
1288 * \param[in] identifier Required identifier of the device.
1289 * \param[in] timeout_ms If the device has not already been seen a scan will
1290 * be performed. This time out is how long we should
1291 * wait to search for the device.
1292 *
1293 * \return Result.
1294 */
1295M_API M_io_error_t M_io_ble_create(M_io_t **io_out, const char *identifier, M_uint64 timeout_ms);
1296
1297
1298/*! Create a ble connection to a device that provides a given service.
1299 *
1300 * This connects to the first device found exposing the requested service.
1301 *
1302 * \param[out] io_out io object for communication.
1303 * \param[in] service_uuid UUID of service.
1304 * \param[in] timeout_ms If the device has not already been seen a scan will
1305 * be performed. This time out is how long we should
1306 * wait to search for the device.
1307 *
1308 * \return Result.
1309 */
1310M_API M_io_error_t M_io_ble_create_with_service(M_io_t **io_out, const char *service_uuid, M_uint64 timeout_ms);
1311
1312
1313/*! Get the device identifier
1314 *
1315 * \param[in] io io object.
1316 *
1317 * \return String.
1318 */
1320
1321
1322/*! Get the device name
1323 *
1324 * \param[in] io io object.
1325 *
1326 * \return String.
1327 */
1328M_API char *M_io_ble_get_name(M_io_t *io);
1329
1330
1331/*! Get a list of service UUIDs provided by the device.
1332 *
1333 * \param[in] io io object.
1334 *
1335 * \return List of strings.
1336 */
1338
1339
1340/*! Get a list of characteristic UUIDs provided a service provided by the device.
1341 *
1342 * \param[in] io io object.
1343 * \param[in] service_uuid UUID of service.
1344 *
1345 * \return List of strings.
1346 */
1347M_API M_list_str_t *M_io_ble_get_service_characteristics(M_io_t *io, const char *service_uuid);
1348
1349
1350
1351/*! Get the properties for the specific characteristic.
1352 *
1353 * \param[in] io io object.
1354 * \param[in] service_uuid UUID of service.
1355 * \param[in] characteristic_uuid UUID of characteristic.
1356 *
1357 * \return Properties
1358 */
1359M_API M_io_ble_property_t M_io_ble_get_characteristic_properties(M_io_t *io, const char *service_uuid, const char *characteristic_uuid);
1360
1361
1362/*! Get the maximum write sizes from an io object.
1363 *
1364 * Queries the highest BLE layer in the stack, if there are more than one.
1365 *
1366 * \param[in] io io object.
1367 * \param[out] with_response The maximum size that will receive a response.
1368 * \param[out] without_response The maximum size that will not receive a response.
1369 */
1370M_API void M_io_ble_get_max_write_sizes(M_io_t *io, size_t *with_response, size_t *without_response);
1371
1372
1373/*! Get the last service and characteristic uuids that generated a write event.
1374 *
1375 * Write with response will generate a write event when the data is written.
1376 * This allows getting the last service and characteristic uuid that generated
1377 * a write event in order to differentiate writes to multiple characteristics.
1378 * Due to write events all being on the same event loop, calling this function
1379 * in a write event will always correspond to the service and characteristic
1380 * that generated the event.
1381 *
1382 * Once called the information will be removed so the next call will be
1383 * information for the next write event. Calling multiple times or not calling
1384 * in a write event can cause the event vs data from this function to become
1385 * out of sync.
1386 *
1387 * Use of this function is optional and is not be necessary if only writing
1388 * once service and one characteristic.
1389 *
1390 * \param[in] io io object.
1391 * \param[out] service_uuid UUID of service.
1392 * \param[out] characteristic_uuid UUID of characteristic.
1393 *
1394 * \return M_TRUE if service_uuid and characteristic_uuid was filled. M_FALSE if not filled due to error
1395 * or because there are no entries. Use M_TRUE to determine if service_uuid and characteristic_uuid
1396 * response parameters should be freed.
1397 */
1398M_API M_bool M_io_ble_get_last_write_characteristic(M_io_t *io, char **service_uuid, char **characteristic_uuid);
1399
1400
1401/*! Get the service associated with a meta object.
1402 *
1403 * \param[in] io io object.
1404 * \param[in] meta Meta.
1405 *
1406 * \return UUID
1407 */
1408M_API const char *M_io_ble_meta_get_service(M_io_t *io, M_io_meta_t *meta);
1409
1410
1411/*! Get the characteristic associated with a meta object.
1412 *
1413 * \param[in] io io object.
1414 * \param[in] meta Meta.
1415 *
1416 * \return UUID
1417 */
1419
1420
1421/*! Get the write type.
1422 *
1423 * \param[in] io io object.
1424 * \param[in] meta Meta.
1425 *
1426 * \return type.
1427 */
1429
1430
1431/*! Get the read type.
1432 *
1433 * \param[in] io io object.
1434 * \param[in] meta Meta.
1435 *
1436 * \return type.
1437 */
1439
1440
1441/*! Get the RSSI value from an RSSI read.
1442 *
1443 * RSSI value is in decibels.
1444 *
1445 * \param[in] io io object.
1446 * \param[in] meta Meta.
1447 * \param[out] rssi RSSI value.
1448 *
1449 * \return M_TRUE if RSSI read. Otherwise, M_FALSE.
1450 */
1451M_API M_bool M_io_ble_meta_get_rssi(M_io_t *io, M_io_meta_t *meta, M_int64 *rssi);
1452
1453
1454/*! Set the service associated with a meta object.
1455 *
1456 * \param[in] io io object.
1457 * \param[in] meta Meta.
1458 * \param[in] service_uuid UUID of service.
1459 */
1460M_API void M_io_ble_meta_set_service(M_io_t *io, M_io_meta_t *meta, const char *service_uuid);
1461
1462
1463/*! Set the characteristic associated with a meta object.
1464 *
1465 * \param[in] io io object.
1466 * \param[in] meta Meta.
1467 * \param[in] characteristic_uuid UUID of characteristic.
1468 */
1469M_API void M_io_ble_meta_set_characteristic(M_io_t *io, M_io_meta_t *meta, const char *characteristic_uuid);
1470
1471
1472/*! Set whether to receive notifications for characteristic data changes
1473 *
1474 * If not called the default is to enable notifications.
1475 *
1476 * Not all characteristic's support notifications. If not supported polling with M_io_write_meta
1477 * using M_IO_BLE_WTYPE_REQVAL is the only way to retrieve the current value.
1478 * \param[in] io io object.
1479 * \param[in] meta Meta.
1480 * \param[in] enable Enable or disable receiving notifications.
1481 */
1482M_API void M_io_ble_meta_set_notify(M_io_t *io, M_io_meta_t *meta, M_bool enable);
1483
1484
1485/*! Set whether a write should be blind.
1486 *
1487 * If the type is not set, the default is to have writes
1488 * wait for confirmation response before subsequent writes will be allowed.
1489 *
1490 * \param[in] io io object.
1491 * \param[in] meta Meta.
1492 * \param[in] type Property controlling
1493 */
1495
1496/*! @} */
1497
1498__END_DECLS
1499
1500#endif /* __M_IO_BLE_H__ */
1501
void(* M_event_callback_t)(M_event_t *event, M_event_type_t type, M_io_t *io, void *cb_arg)
Definition: m_event.h:227
struct M_event M_event_t
Definition: m_event.h:210
struct M_io_ble_enum M_io_ble_enum_t
Definition: m_io_ble.h:1175
void M_io_ble_meta_set_write_type(M_io_t *io, M_io_meta_t *meta, M_io_ble_wtype_t type)
M_io_ble_rtype_t M_io_ble_meta_get_read_type(M_io_t *io, M_io_meta_t *meta)
M_io_ble_wtype_t
Definition: m_io_ble.h:1135
M_bool M_io_ble_scan(M_event_t *event, M_event_callback_t callback, void *cb_data, M_uint64 timeout_ms)
const char * M_io_ble_enum_name(const M_io_ble_enum_t *btenum, size_t idx)
M_time_t M_io_ble_enum_last_seen(const M_io_ble_enum_t *btenum, size_t idx)
M_io_error_t M_io_ble_create_with_service(M_io_t **io_out, const char *service_uuid, M_uint64 timeout_ms)
M_list_str_t * M_io_ble_get_services(M_io_t *io)
const char * M_io_ble_meta_get_characteristic(M_io_t *io, M_io_meta_t *meta)
M_list_str_t * M_io_ble_get_service_characteristics(M_io_t *io, const char *service_uuid)
const char * M_io_ble_meta_get_service(M_io_t *io, M_io_meta_t *meta)
M_bool M_io_ble_meta_get_rssi(M_io_t *io, M_io_meta_t *meta, M_int64 *rssi)
char * M_io_ble_get_identifier(M_io_t *io)
M_io_error_t M_io_ble_create(M_io_t **io_out, const char *identifier, M_uint64 timeout_ms)
M_bool M_io_ble_get_last_write_characteristic(M_io_t *io, char **service_uuid, char **characteristic_uuid)
size_t M_io_ble_enum_count(const M_io_ble_enum_t *btenum)
const char * M_io_ble_enum_identifier(const M_io_ble_enum_t *btenum, size_t idx)
M_io_ble_enum_t * M_io_ble_enum(void)
void M_io_ble_meta_set_service(M_io_t *io, M_io_meta_t *meta, const char *service_uuid)
M_io_ble_property_t
Definition: m_io_ble.h:1165
M_io_ble_property_t M_io_ble_get_characteristic_properties(M_io_t *io, const char *service_uuid, const char *characteristic_uuid)
void M_io_ble_enum_destroy(M_io_ble_enum_t *btenum)
char * M_io_ble_get_name(M_io_t *io)
M_io_ble_wtype_t M_io_ble_meta_get_write_type(M_io_t *io, M_io_meta_t *meta)
void M_io_ble_get_max_write_sizes(M_io_t *io, size_t *with_response, size_t *without_response)
M_list_str_t * M_io_ble_enum_service_uuids(const M_io_ble_enum_t *btenum, size_t idx)
void M_io_ble_meta_set_notify(M_io_t *io, M_io_meta_t *meta, M_bool enable)
void M_io_ble_meta_set_characteristic(M_io_t *io, M_io_meta_t *meta, const char *characteristic_uuid)
M_io_ble_rtype_t
Definition: m_io_ble.h:1151
@ M_IO_BLE_WTYPE_REQNOTIFY
Definition: m_io_ble.h:1143
@ M_IO_BLE_WTYPE_REQVAL
Definition: m_io_ble.h:1139
@ M_IO_BLE_WTYPE_WRITE
Definition: m_io_ble.h:1136
@ M_IO_BLE_WTYPE_WRITENORESP
Definition: m_io_ble.h:1138
@ M_IO_BLE_WTYPE_REQRSSI
Definition: m_io_ble.h:1142
@ M_IO_BLE_PROPERTY_WRITE
Definition: m_io_ble.h:1168
@ M_IO_BLE_PROPERTY_WRITENORESP
Definition: m_io_ble.h:1169
@ M_IO_BLE_PROPERTY_READ
Definition: m_io_ble.h:1167
@ M_IO_BLE_PROPERTY_NONE
Definition: m_io_ble.h:1166
@ M_IO_BLE_PROPERTY_NOTIFY
Definition: m_io_ble.h:1170
@ M_IO_BLE_RTYPE_RSSI
Definition: m_io_ble.h:1153
@ M_IO_BLE_RTYPE_NOTIFY
Definition: m_io_ble.h:1154
@ M_IO_BLE_RTYPE_READ
Definition: m_io_ble.h:1152
enum M_io_error M_io_error_t
Definition: m_io.h:93
struct M_io M_io_t
Definition: m_io.h:59
struct M_io_meta M_io_meta_t
Definition: m_io.h:64
struct M_list_str M_list_str_t
Definition: m_list_str.h:80
M_int64 M_time_t
Definition: m_time.h:161