|
| 1 | +/** |
| 2 | + * The MIT License (MIT) |
| 3 | + * |
| 4 | + * 2014 - Tom Magnier <magnier.tom@gmail.com> |
| 5 | + * |
| 6 | + * Adapted from ll-initiator example |
| 7 | + * |
| 8 | + * This example demonstrates the connection state, in Central role. |
| 9 | + * A GATT characteristic (handle 0x000E) is written regularly and notifications |
| 10 | + * from the battery service (characteristic : handle 0x0015) are received. |
| 11 | + * |
| 12 | + * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 13 | + * of this software and associated documentation files (the "Software"), to deal |
| 14 | + * in the Software without restriction, including without limitation the rights |
| 15 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 16 | + * copies of the Software, and to permit persons to whom the Software is |
| 17 | + * furnished to do so, subject to the following conditions: |
| 18 | + * |
| 19 | + * The above copyright notice and this permission notice shall be included in all |
| 20 | + * copies or substantial portions of the Software. |
| 21 | + * |
| 22 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 23 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 24 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 25 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 26 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 27 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 28 | + * SOFTWARE. |
| 29 | + */ |
| 30 | + |
| 31 | +#include <stdint.h> |
| 32 | +#include <string.h> |
| 33 | +#include <stdio.h> |
| 34 | + |
| 35 | +#include <blessed/bdaddr.h> |
| 36 | +#include <blessed/log.h> |
| 37 | +#include <blessed/events.h> |
| 38 | +#include <blessed/evtloop.h> |
| 39 | + |
| 40 | +#include "delay.h" |
| 41 | + |
| 42 | +#include "ll.h" |
| 43 | + |
| 44 | +#define SCAN_WINDOW 200000 |
| 45 | +#define SCAN_INTERVAL 500000 |
| 46 | + |
| 47 | +/* L2CAP specification, Section 3.1, Core 4.1 p.1683 |
| 48 | + * L2CAP specification, Section 1.1, Core 4.1 p.1677 |
| 49 | + * ATT specification, Section 3.4.5.3, Core 4.1 p. 2148 */ |
| 50 | +static uint8_t out_buffer[] = { |
| 51 | + 0x04, 0x00, //L2CAP payload length |
| 52 | + 0x04, 0x00, //L2CAP Channel ID : ATT |
| 53 | + 0x52, //ATT opcode : write cmd |
| 54 | + 0x0E, 0x00, //ATT handle (0x000E) |
| 55 | + 0x00}; //ATT value to write |
| 56 | + |
| 57 | +static uint8_t in_buffer[LL_DATA_MTU_PAYLOAD]; |
| 58 | + |
| 59 | +/* PDU to send to enable notifications on battery service */ |
| 60 | +static uint8_t cccd_out_buffer[] = { |
| 61 | + 0x05, 0x00, //L2CAP payload length |
| 62 | + 0x04, 0x00, //L2CAP Channel ID : ATT |
| 63 | + 0x52, //ATT opcode : write cmd |
| 64 | + 0x16, 0x00, //ATT handle (0x0016) |
| 65 | + 0x01, 0x00}; //ATT value to write |
| 66 | + |
| 67 | +/* Expected packet on handle value notification ; the 8th byte is the value */ |
| 68 | +static const uint8_t notification_buffer_value[] = { |
| 69 | + 0x04, 0x00, //L2CAP payload length |
| 70 | + 0x04, 0x00, //L2CAP CID : ATT |
| 71 | + 0x1B, //ATT opcode : handle value notification |
| 72 | + 0x15, 0x00}; //ATT handle (0x0015) |
| 73 | + |
| 74 | +static const bdaddr_t addr = { { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, |
| 75 | + BDADDR_TYPE_RANDOM }; |
| 76 | + |
| 77 | +static bdaddr_t peer_addr; |
| 78 | +uint8_t led_value = 0x00; |
| 79 | + |
| 80 | + |
| 81 | +static __inline const char *format_address(const uint8_t *data) |
| 82 | +{ |
| 83 | + static char address[18]; |
| 84 | + uint8_t i; |
| 85 | + |
| 86 | + for (i = 0; i < 5; i++) |
| 87 | + sprintf(address + 3*i, "%02x:", data[5-i]); |
| 88 | + sprintf(address + 3*i, "%02x", data[5-i]); |
| 89 | + |
| 90 | + return address; |
| 91 | +} |
| 92 | + |
| 93 | +static __inline const char *format_data(const uint8_t *data, uint8_t len) |
| 94 | +{ |
| 95 | + static char output[3 * LL_ADV_MTU_DATA + 1]; |
| 96 | + uint8_t i; |
| 97 | + |
| 98 | + for (i = 0; i < len; i++) |
| 99 | + sprintf(output + 3*i, "%02x ", data[i]); |
| 100 | + |
| 101 | + output[3*i] = '\0'; |
| 102 | + |
| 103 | + return output; |
| 104 | +} |
| 105 | + |
| 106 | +void conn_evt_cb(ble_evt_t type, const void *data) |
| 107 | +{ |
| 108 | + const ble_evt_ll_connection_complete_t* conn_compl = data; |
| 109 | + const ble_evt_ll_disconnect_complete_t* disconn_compl = data; |
| 110 | + const ble_evt_ll_packets_received_t* packets_rx = data; |
| 111 | + |
| 112 | + switch (type) { |
| 113 | + case BLE_EVT_LL_CONNECTION_COMPLETE: |
| 114 | + DBG("Connection complete, index %u, address %s", |
| 115 | + conn_compl->index, format_address(conn_compl->peer_addr.addr)); |
| 116 | + |
| 117 | + /*Enable notifications on battery service (write 0x0001 on CCCD |
| 118 | + * characteristic, handle 0x0016) */ |
| 119 | + ll_conn_send(cccd_out_buffer, 9); |
| 120 | + break; |
| 121 | + |
| 122 | + case BLE_EVT_LL_DISCONNECT_COMPLETE: |
| 123 | + DBG("Disconnect complete, index %u, reason %02x", |
| 124 | + disconn_compl->index, disconn_compl->reason); |
| 125 | + |
| 126 | + evt_loop_run(); |
| 127 | + break; |
| 128 | + |
| 129 | + case BLE_EVT_LL_PACKETS_SENT: |
| 130 | + out_buffer[7] = led_value; |
| 131 | + ll_conn_send(out_buffer, 8); |
| 132 | + break; |
| 133 | + |
| 134 | + case BLE_EVT_LL_PACKETS_RECEIVED: |
| 135 | + if (!memcmp(in_buffer, notification_buffer_value, 7)) |
| 136 | + DBG("Battery value : %u %%", in_buffer[7]); |
| 137 | + else |
| 138 | + DBG("Received packet : %s", format_data(in_buffer, |
| 139 | + packets_rx->length)); |
| 140 | + break; |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +void adv_report_cb(struct adv_report *report) |
| 145 | +{ |
| 146 | + DBG("adv type %02x, addr type %02x", report->type, report->addr.type); |
| 147 | + DBG("address %s, data %s", format_address(report->addr.addr), |
| 148 | + format_data(report->data, report->len)); |
| 149 | + |
| 150 | + memcpy(peer_addr.addr, report->addr.addr, BDADDR_LEN); |
| 151 | + peer_addr.type = report->addr.type; |
| 152 | + |
| 153 | + ll_scan_stop(); |
| 154 | + ll_conn_create(SCAN_INTERVAL, SCAN_WINDOW, &peer_addr, 1, in_buffer, |
| 155 | + conn_evt_cb); |
| 156 | +} |
| 157 | + |
| 158 | +int main(void) |
| 159 | +{ |
| 160 | + log_init(); |
| 161 | + ll_init(&addr); |
| 162 | + |
| 163 | + DBG("End init, connection + battery notification"); |
| 164 | + |
| 165 | + ll_scan_start(LL_SCAN_PASSIVE, SCAN_INTERVAL, SCAN_WINDOW, |
| 166 | + adv_report_cb); |
| 167 | + while (1) |
| 168 | + { |
| 169 | + led_value++; |
| 170 | + delay(10000); |
| 171 | + } |
| 172 | + |
| 173 | + return 0; |
| 174 | +} |
0 commit comments