I am able to subscribe for differen
#if 1 /* main.c - Application main entry point */ /* * Copyright (c) 2020 SixOctets Systems * * SPDX-License-Identifier: Apache-2.0 */ #include <stdio.h> #include <stddef.h> #include <errno.h> #include <zephyr/zephyr.h> #include <zephyr/sys/printk.h> #include <zephyr/sys/byteorder.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> #include <zephyr/bluetooth/conn.h> #include <zephyr/bluetooth/uuid.h> #include <zephyr/bluetooth/gatt.h> static int scan_start(void); static uint16_t CTS_read_handle =0xFFU; static uint16_t fever_read_handle =0xFFU; #define BT_TEMPERATURE_ADDR_LE_ANY ((bt_addr_le_t[]) { {BT_ADDR_LE_RANDOM, { { 0xEE, 0x4D, 0xAE, 0x55, 0x38, 0xE4 } } } }) //Defining the Temperature alert service UUIDs #define BT_UUID_TEMPERATURE_ALERT_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x1b561960, 0xdf3c, 0x4d2a, 0xb319, 0x8f26bee36772)) #define BT_UUID_TEMPERATURE_ALERT_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001961, 0x0000, 0x1000, 0x8000, 0x0805f9b34fb)) #define BT_UUID_TEMPERATURE_ALERT_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature battery service UUIDs #define BT_UUID_TEMPERATURE_BATTERY_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x0000180F, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_BATTERY_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002A19, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_BATTERY_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature Measurement UUIDs #define BT_UUID_TEMPERATURE_MEASUREMENT_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001809, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_MEASUREMENT_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002A1C, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_MEASUREMENT_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature MOVEMENT UUIDs #define BT_UUID_TEMPERATURE_MOVEMENT_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xe68f1980, 0x8e6d, 0x4254, 0x9650, 0xa4c165f927c8)) #define BT_UUID_TEMPERATURE_POSITION_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001981, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_POSITION_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature Fever and color alert UUIDs #define BT_UUID_TEMPERATURE_COLOR_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xc67b1970, 0x5a63, 0x41d4, 0x9f35, 0xf5218e4c6c83)) #define BT_UUID_TEMPERATURE_FEVER_ALERT_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001972, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) struct bt_conn *default_conn; struct bt_uuid_16 uuid = BT_UUID_INIT_16(0); struct bt_gatt_discover_params bas_discover_params; struct bt_gatt_discover_params hts_discover_params; struct bt_gatt_discover_params alert_discover_params; struct bt_gatt_discover_params pos_discover_params; struct bt_gatt_discover_params fev_cts_discover_params; struct bt_gatt_discover_params cts_discover_params; struct bt_gatt_subscribe_params bas_subscribe_params; struct bt_gatt_subscribe_params hts_subscribe_params; struct bt_gatt_subscribe_params alert_subscribe_params; struct bt_gatt_subscribe_params pos_subscribe_params; struct bt_gatt_read_params read_params; struct bt_gatt_write_params CTS_write; struct bt_gatt_write_params fever_alert_write; struct bt_uuid_128 uuid_temp = BT_UUID_INIT_128(0); static const uint8_t fever_temperature_limit[] = {0x00,0x0F}; //38.4 C max temperature uint8_t value[10]={0xE6,0x07,0x0C,0x1D, 0x0C,0x01, 0x00,0x01, 0x00,0x00}; static uint8_t discover_func_temperature(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static uint8_t discover_func_position(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static uint8_t discover_func_alert(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static uint8_t discover_func_battery(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static uint8_t discover_func_fev_alert(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static uint8_t discover_func_CTS(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params); static double pow(double x, double y) { double result = 1; if (y < 0) { y = -y; while (y--) { result /= x; } } else { while (y--) { result *= x; } } return result; } static uint8_t notify_func_temp(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { double temperature; uint32_t mantissa; int8_t exponent; printk("Temperature notify\n"); // if (!data) { // printk("[UNSUBSCRIBED]\n"); // params->value_handle = 0U; // return BT_GATT_ITER_STOP; // } uint8_t *d = (uint8_t *)data; for (int i =0; i < length; i++){ printk("%2x ", d[i]); } printk("\n"); // /* temperature value display */ // mantissa = sys_get_le24(&((uint8_t *)data)[1]); // exponent = ((uint8_t *)data)[4]; // temperature = (double)mantissa * pow(10, exponent); // printf("Temperature %gC.\n", temperature); return BT_GATT_ITER_CONTINUE; } static uint8_t notify_func_battery(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { printk("Battery notify\n"); // if (!data) { // printk("[UNSUBSCRIBED]\n"); // params->value_handle = 0U; // return BT_GATT_ITER_STOP; // } uint8_t *d = (uint8_t *)data; for (int i =0; i < length; i++){ printk("%2x ", d[i]); } printk("\n"); return BT_GATT_ITER_CONTINUE; } static uint8_t notify_func_position(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { printk("Position notify\n"); // if (!data) { // printk("[UNSUBSCRIBED]\n"); // params->value_handle = 0U; // return BT_GATT_ITER_STOP; // } uint8_t *d = (uint8_t *)data; for (int i =0; i < length; i++){ printk("%2x ", d[i]); } printk("\n"); return BT_GATT_ITER_CONTINUE; } static uint8_t notify_func_alert(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { printk("Alert notify\n"); // if (!data) { // printk("[UNSUBSCRIBED]\n"); // params->value_handle = 0U; // return BT_GATT_ITER_STOP; // } uint8_t *d = (uint8_t *)data; for (int i =0; i < length; i++){ printk("%2x ", d[i]); } printk("\n"); return BT_GATT_ITER_CONTINUE; } static uint8_t read_func_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params, const void *data, uint16_t length) { if ((data != NULL) && (err == 0)) { if (params->single.handle== CTS_read_handle) { uint8_t *d = (uint8_t *)data; printk("length %2x \n",length); for (int i =0; i < length; i++){ printk("%2x ", d[i]); } } printk("\n"); }else{ printk("No data\n"); } return BT_GATT_ITER_STOP; } static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params) { if (err != BT_ATT_ERR_SUCCESS) { printk("Write failed: 0x%02X\n", err); }else{ printk("\nafter Write CTS sucess\n"); } } static void write_fever_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params) { if (err != BT_ATT_ERR_SUCCESS) { printk("Write failed: 0x%02X\n", err); }else{ printk("Write sucess \n"); } } static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { printk("%s: to level %u (err %u)\n", __func__, level, err); } static uint8_t discover_func_temperature(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("temperature [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(hts_discover_params.uuid, BT_UUID_HTS)) { memcpy(&uuid, BT_UUID_HTS_MEASUREMENT, sizeof(uuid)); hts_discover_params.uuid = &uuid.uuid; hts_discover_params.start_handle = attr->handle + 1; hts_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &hts_discover_params); if (err) { printk("Discover failed 1(err %d)\n", err); } } else if (!bt_uuid_cmp(hts_discover_params.uuid,BT_UUID_HTS_MEASUREMENT)) { memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid)); hts_discover_params.uuid = &uuid.uuid; hts_discover_params.start_handle = attr->handle + 2; hts_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; hts_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &hts_discover_params); if (err) { printk("Discover failed 2(err %d)\n", err); } } else { hts_subscribe_params.notify = notify_func_temp; hts_subscribe_params.value = BT_GATT_CCC_INDICATE; hts_subscribe_params.min_security = bt_conn_get_security(conn); hts_subscribe_params.ccc_handle = attr->handle ; err = bt_gatt_subscribe(conn, &hts_subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("temperature measurement [SUBSCRIBED]\n"); } memcpy(&uuid_temp, BT_UUID_TEMPERATURE_MOVEMENT_SERVICE, sizeof(uuid_temp)); pos_discover_params.uuid = &uuid_temp.uuid; pos_discover_params.func = discover_func_position; pos_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; pos_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; pos_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &pos_discover_params); if (err) { printk("Discover failed(err %d)\n", err); return; } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } static uint8_t discover_func_battery(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("Battery [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(bas_discover_params.uuid, BT_UUID_TEMPERATURE_BATTERY_SERVICE)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_BATTERY_CHARACTER, sizeof(uuid_temp)); bas_discover_params.uuid = &uuid_temp.uuid; bas_discover_params.start_handle = attr->handle + 1; bas_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &bas_discover_params); if (err) { printk("Discover failed 3 (err %d)\n", err); } } else if (!bt_uuid_cmp(bas_discover_params.uuid, BT_UUID_TEMPERATURE_BATTERY_CHARACTER)) { memcpy(&uuid_temp, BT_UUID_GATT_CCC, sizeof(uuid_temp)); bas_discover_params.uuid = &uuid_temp.uuid; bas_discover_params.start_handle = attr->handle + 2; bas_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; bas_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &bas_discover_params); if (err) { printk("Discover failed 4(err %d)\n", err); } } else { bas_subscribe_params.notify= notify_func_battery; bas_subscribe_params.value = BT_GATT_CCC_NOTIFY; bas_subscribe_params.min_security = bt_conn_get_security(conn); bas_subscribe_params.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &bas_subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("Battery characteristics [SUBSCRIBED]\n"); } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } static uint8_t discover_func_alert(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if(!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("alert [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(alert_discover_params.uuid, BT_UUID_TEMPERATURE_ALERT_SERVICE)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_ALERT_CHARACTER, sizeof(uuid_temp)); alert_discover_params.uuid = &uuid_temp.uuid; alert_discover_params.start_handle = attr->handle + 1; alert_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &alert_discover_params); if (err) { printk("Discover failed 5 (err %d)\n", err); } } else if (!bt_uuid_cmp(alert_discover_params.uuid, BT_UUID_TEMPERATURE_ALERT_CHARACTER)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_ALERT_CCC, sizeof(uuid_temp)); alert_discover_params.uuid = &uuid_temp.uuid; alert_discover_params.start_handle = attr->handle + 2; alert_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; alert_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &alert_discover_params); if (err) { printk("Discover failed 6(err %d)\n", err); } } else { alert_subscribe_params.notify = notify_func_alert; alert_subscribe_params.value = BT_GATT_CCC_INDICATE; alert_subscribe_params.min_security = bt_conn_get_security(conn); alert_subscribe_params.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &alert_subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("Alert characteristics [SUBSCRIBED]\n"); } memcpy(&uuid_temp, BT_UUID_TEMPERATURE_COLOR_SERVICE, sizeof(uuid_temp)); fev_cts_discover_params.uuid = &uuid_temp.uuid; fev_cts_discover_params.func = discover_func_fev_alert; fev_cts_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; fev_cts_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; fev_cts_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &fev_cts_discover_params); if (err) { printk("Discover failed(err %d)\n", err); return; } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } static uint8_t discover_func_position(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("position [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(pos_discover_params.uuid, BT_UUID_TEMPERATURE_MOVEMENT_SERVICE)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_POSITION_CHARACTER, sizeof(uuid_temp)); pos_discover_params.uuid = &uuid_temp.uuid; pos_discover_params.start_handle = attr->handle + 1; pos_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &pos_discover_params); if (err) { printk("Discover failed 7(err %d)\n", err); } } else if (!bt_uuid_cmp(pos_discover_params.uuid, BT_UUID_TEMPERATURE_POSITION_CHARACTER)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_POSITION_CCC, sizeof(uuid_temp)); pos_discover_params.uuid = &uuid_temp.uuid; pos_discover_params.start_handle = attr->handle + 2; pos_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; pos_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &pos_discover_params); if (err) { printk("Discover failed 8(err %d)\n", err); } } else { pos_subscribe_params.notify = notify_func_position; pos_subscribe_params.value = BT_GATT_CCC_INDICATE; pos_subscribe_params.min_security = bt_conn_get_security(conn); pos_subscribe_params.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &pos_subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("position characteristics [SUBSCRIBED]\n"); } memcpy(&uuid_temp, BT_UUID_TEMPERATURE_ALERT_SERVICE, sizeof(uuid_temp)); alert_discover_params.uuid = &uuid_temp.uuid; alert_discover_params.func = discover_func_alert; alert_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; alert_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; alert_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &alert_discover_params); if (err) { printk("Discover failed(err %d)\n", err); } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } static uint8_t discover_func_fev_alert(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("fev alert [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(fev_cts_discover_params.uuid, BT_UUID_TEMPERATURE_COLOR_SERVICE)) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_FEVER_ALERT_CHARACTER, sizeof(uuid_temp)); fev_cts_discover_params.uuid = &uuid_temp.uuid; fev_cts_discover_params.start_handle = attr->handle + 1; fev_cts_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &fev_cts_discover_params); if (err) { printk("Discover failed 9(err %d)\n", err); } } else if (!bt_uuid_cmp(fev_cts_discover_params.uuid, BT_UUID_TEMPERATURE_FEVER_ALERT_CHARACTER)) { fever_alert_write.handle = bt_gatt_attr_value_handle(attr);; fever_alert_write.func = write_fever_cb; fever_alert_write.offset = 0; fever_alert_write.data = fever_temperature_limit; fever_alert_write.length = sizeof(fever_temperature_limit); // UNSET_FLAG(flag_write_complete); printk("bt write start \n"); err = bt_gatt_write(conn, &fever_alert_write); if(err){ printk("write failed (err %d)\n", err); return BT_GATT_ITER_STOP; } memcpy(&uuid, BT_UUID_CTS, sizeof(uuid)); fev_cts_discover_params.uuid = &uuid.uuid; fev_cts_discover_params.start_handle = attr->handle + 2; fev_cts_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &fev_cts_discover_params); if (err) { printk("Discover failed(err %d)\n", err); } memcpy(&uuid, BT_UUID_CTS, sizeof(uuid)); cts_discover_params.uuid = &uuid.uuid; cts_discover_params.func = discover_func_CTS; cts_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; cts_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; cts_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &cts_discover_params); if (err) { printk("Discover failed(err %d)\n", err); } } return BT_GATT_ITER_STOP; } static uint8_t discover_func_CTS(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("CTS [ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(cts_discover_params.uuid, BT_UUID_CTS)) { memcpy(&uuid, BT_UUID_CTS_CURRENT_TIME, sizeof(uuid)); cts_discover_params.uuid = &uuid.uuid; cts_discover_params.start_handle = attr->handle + 1; cts_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &cts_discover_params); if (err) { printk("Discover failed 10(err %d)\n", err); } } else if (!bt_uuid_cmp(cts_discover_params.uuid, BT_UUID_CTS_CURRENT_TIME)) { /* Current time characteristic discovered */ CTS_read_handle = bt_gatt_attr_value_handle(attr); printk("before write CTS \n"); /* Read Tx Power */ read_params.func = read_func_cb; read_params.handle_count = 1; read_params.single.handle = CTS_read_handle; read_params.single.offset = 0; err = bt_gatt_read(conn, &read_params); if (err) { printk("GATT Read failed (err %d)\n", err); } CTS_read_handle =bt_gatt_attr_value_handle(attr); CTS_write.handle = CTS_read_handle; CTS_write.func = write_cb; CTS_write.offset = 0; CTS_write.data = value; CTS_write.length = sizeof(value); err = bt_gatt_write(conn, &CTS_write); if(err){ printk("write failed (err %d)\n", err); return BT_GATT_ITER_STOP; } /* Current time characteristic discovered */ CTS_read_handle = bt_gatt_attr_value_handle(attr); /* Read Tx Power */ read_params.func = read_func_cb; read_params.handle_count = 1; read_params.single.handle = CTS_read_handle; read_params.single.offset = 0; err = bt_gatt_read(conn, &read_params); if (err) { printk("GATT Read failed (err %d)\n", err); } } return BT_GATT_ITER_STOP; } static void connected(struct bt_conn *conn, uint8_t conn_err) { char addr[BT_ADDR_LE_STR_LEN]; int err; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (conn_err) { printk("Failed to connect to %s (%u)\n", addr, conn_err); bt_conn_unref(default_conn); default_conn = NULL; scan_start(); return; } printk("Connected: %s\n", addr); err = bt_conn_set_security(conn, BT_SECURITY_L2); if (err) { printk("bt_conn_set_security failed (err %d)\n", err); return; } if (conn == default_conn) { memcpy(&uuid_temp, BT_UUID_TEMPERATURE_BATTERY_SERVICE, sizeof(uuid_temp)); bas_discover_params.uuid = &uuid_temp.uuid; bas_discover_params.func = discover_func_battery; bas_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; bas_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; bas_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &bas_discover_params); if (err) { printk("Discover failed(err %d)\n", err); //return BT_GATT_ITER_STOP; } memcpy(&uuid_temp, BT_UUID_TEMPERATURE_MEASUREMENT_SERVICE, sizeof(uuid_temp)); hts_discover_params.uuid = &uuid_temp.uuid; hts_discover_params.func = discover_func_temperature; hts_discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; hts_discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; hts_discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &hts_discover_params); if (err) { printk("Discover failed(err %d)\n", err); } } } static bool eir_found(struct bt_data *data, void *user_data) { bt_addr_le_t *addr = user_data; int i; printk("[AD]: %u data_len %u\n", data->type, data->data_len); switch (data->type) { case BT_DATA_UUID16_SOME: case BT_DATA_UUID16_ALL: if (data->data_len % sizeof(uint16_t) != 0U) { printk("AD malformed\n"); return true; } for (i = 0; i < data->data_len; i += sizeof(uint16_t)) { struct bt_uuid *uuid; uint16_t u16; int err; memcpy(&u16, &data->data[i], sizeof(u16)); uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16)); if (bt_uuid_cmp(uuid, BT_UUID_HTS)) { printk("HTS UUID matched \n"); continue; } err = bt_le_scan_stop(); if (err) { printk("Stop LE scan failed (err %d)\n", err); continue; } err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn); if (err) { printk("Create connection failed (err %d)\n", err); scan_start(); } return false; } } return true; } static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { char dev[BT_ADDR_LE_STR_LEN]; if(bt_addr_cmp(BT_TEMPERATURE_ADDR_LE_ANY, addr) == 0){ bt_addr_le_to_str(addr, dev, sizeof(dev)); printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", dev, type, ad->len, rssi); /* We're only interested in connectable events */ if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) { bt_data_parse(ad, eir_found, (void *)addr); } } } static int scan_start(void) { /* Use active scanning and disable duplicate filtering to handle any * devices that might update their advertising data at runtime. */ struct bt_le_scan_param scan_param = { .type = BT_LE_SCAN_TYPE_ACTIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = BT_GAP_SCAN_FAST_INTERVAL, .window = BT_GAP_SCAN_FAST_WINDOW, }; return bt_le_scan_start(&scan_param, device_found); } static void disconnected(struct bt_conn *conn, uint8_t reason) { char addr[BT_ADDR_LE_STR_LEN]; int err; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); if (default_conn != conn) { return; } bt_conn_unref(default_conn); default_conn = NULL; err = scan_start(); if (err) { printk("Scanning failed to start (err %d)\n", err); } } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, .security_changed =security_changed, }; void main(void) { int err; printk("init main \n"); err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } printk("Bluetooth initialized\n"); err = scan_start(); if (err) { printk("Scanning failed to start (err %d)\n", err); return; } printk("Scanning successfully started\n"); } #else /* main.c - Application main entry point */ /* * Copyright (c) 2015-2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/types.h> #include <stddef.h> #include <errno.h> #include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> #include <zephyr/bluetooth/conn.h> #include <zephyr/bluetooth/uuid.h> #include <zephyr/bluetooth/gatt.h> #include <zephyr/sys/byteorder.h> static void start_scan(void); #define NAME_LEN 30 #define SPO2_NAME (char[9]){"BerryMed"} #define BT_TEMPERATURE_ADDR_LE_ANY ((bt_addr_le_t[]) { { BT_ADDR_LE_RANDOM, { { 0xEE, 0x4D, 0xAE, 0x55, 0x38, 0xE4 } } } }) //Defining the different UUIDs for the Services of Wireless Temperature "tucky" //Defining the Temperature alert service UUIDs #define BT_UUID_TEMPERATURE_ALERT_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x1b561960, 0xdf3c, 0x4d2a, 0xb319, 0x8f26bee36772)) #define BT_UUID_TEMPERATURE_ALERT_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001961, 0x0000, 0x1000, 0x8000, 0x0805f9b34fb)) #define BT_UUID_TEMPERATURE_ALERT_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature battery service UUIDs #define BT_UUID_TEMPERATURE_BATTERY_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x0000180F, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_BATTERY_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002A19, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_BATTERY_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) //Defining the Temperature Measurement UUIDs #define BT_UUID_TEMPERATURE_MEASUREMENT_SERVICE \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00001809, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_MEASUREMENT_CHARACTER \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002A1C, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) #define BT_UUID_TEMPERATURE_MEASUREMENT_CCC \ BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00002902, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)) static struct bt_conn *default_conn; static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0); static struct bt_gatt_discover_params discover_params; static struct bt_gatt_subscribe_params subscribe_params; static uint8_t notify_func(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { printk("Temperature notify\n"); // if (!data) { // printk("[UNSUBSCRIBED]\n"); // params->value_handle = 0U; // return BT_GATT_ITER_STOP; // } uint8_t *d = (uint8_t *)data; for (int i =0; i < length; i++){ printk("%2x ", d[i]); } printk("\n"); return BT_GATT_ITER_CONTINUE; } static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params){ int err; printk("Discovering...\n"); printk("attr: %u\n", (unsigned int)attr); if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_STOP; } printk("[ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_TEMPERATURE_ALERT_SERVICE)) { memcpy(&uuid, BT_UUID_TEMPERATURE_ALERT_CHARACTER, sizeof(uuid)); discover_params.uuid = &uuid.uuid; discover_params.start_handle = attr->handle + 1; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &discover_params); if (err) { printk("Discover failed (err %d)\n", err); } } else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_TEMPERATURE_ALERT_CHARACTER)) { memcpy(&uuid, BT_UUID_TEMPERATURE_ALERT_CCC, sizeof(uuid)); discover_params.uuid = &uuid.uuid; discover_params.start_handle = attr->handle + 2; discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &discover_params); if (err) { printk("Discover failed (err %d)\n", err); } } else { subscribe_params.notify = notify_func; subscribe_params.value = BT_GATT_CCC_INDICATE; subscribe_params.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("[SUBSCRIBED]\n"); //ble_spo2_conn_sucess = true; //blow_buzzer(); //k_msleep(500); } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } static uint8_t discover_func_HTS(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { int err; printk("Discovering...\n"); if (!attr) { printk("Discover complete\n"); (void)memset(params, 0, sizeof(*params)); return BT_GATT_ITER_CONTINUE; } printk("[ATTRIBUTE] handle %u\n", attr->handle); if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_TEMPERATURE_MEASUREMENT_SERVICE)) { memcpy(&uuid, BT_UUID_TEMPERATURE_MEASUREMENT_CHARACTER, sizeof(uuid)); discover_params.uuid = &uuid.uuid; discover_params.start_handle = attr->handle + 1; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(conn, &discover_params); if (err) { printk("Discover failed (err %d)\n", err); } } else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_TEMPERATURE_MEASUREMENT_CHARACTER)) { memcpy(&uuid, BT_UUID_TEMPERATURE_MEASUREMENT_CCC, sizeof(uuid)); discover_params.uuid = &uuid.uuid; discover_params.start_handle = attr->handle + 2; discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); err = bt_gatt_discover(conn, &discover_params); if (err) { printk("Discover failed (err %d)\n", err); } } else { subscribe_params.notify = notify_func; subscribe_params.value = BT_GATT_CCC_INDICATE; subscribe_params.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &subscribe_params); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("[SUBSCRIBED]\n"); } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } // static bool eir_found(struct bt_data *data, void *user_data) // { // bt_addr_le_t *addr = user_data; // char name[NAME_LEN]; // uint8_t len; // int err1; // struct bt_le_conn_param *param_spo2; // (void)memset(name, 0, sizeof(name)); // printk("[AD]: %u data_len %u\n", data->type, data->data_len); // switch (data->type) { // case BT_DATA_NAME_SHORTENED: // case BT_DATA_NAME_COMPLETE: // len = MIN(data->data_len, NAME_LEN - 1); // (void)memcpy(name, data->data, len); // name[len] = '\0'; // if((memcmp(SPO2_NAME,name,sizeof(SPO2_NAME)))==0){ // err1 = bt_le_scan_stop(); // if (err1) { // printk("Stop LE scan failed (err %d)\n", err1); // } // param_spo2 = BT_LE_CONN_PARAM_DEFAULT; // err1 = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, // param_spo2, &default_conn); // if (err1) { // printk("Create conn failed (err %d)\n", err1); // start_scan(); // } // return false; // } // default: return true; // } // return true; // } static bool eir_found(struct bt_data *data, void *user_data) { bt_addr_le_t *addr = user_data; int i; printk("[AD]: %u data_len %u\n", data->type, data->data_len); switch (data->type) { case BT_DATA_UUID16_SOME: case BT_DATA_UUID16_ALL: if (data->data_len % sizeof(uint16_t) != 0U) { printk("AD malformed\n"); return true; } for (i = 0; i < data->data_len; i += sizeof(uint16_t)) { struct bt_uuid *uuid; uint16_t u16; int err; memcpy(&u16, &data->data[i], sizeof(u16)); uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16)); if (bt_uuid_cmp(uuid, BT_UUID_HTS)) { continue; } err = bt_le_scan_stop(); if (err) { printk("Stop LE scan failed (err %d)\n", err); continue; } err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn); if (err) { printk("Create connection failed (err %d)\n", err); scan_start(); } return false; } } return true; } static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { char dev[BT_ADDR_LE_STR_LEN]; int err; if(bt_addr_le_cmp(BT_TEMPERATURE_ADDR_LE_ANY,addr) == 0){ bt_addr_le_to_str(addr, dev, sizeof(dev)); printk("\n"); printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", dev, type, ad->len, rssi); /* We're only interested in connectable events */ // if (type == BT_GAP_ADV_TYPE_ADV_IND || // type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { // bt_data_parse(ad, eir_found, (void *)addr); // } err = bt_le_scan_stop(); if (err) { printk("Stop LE scan failed (err %d)\n", err); } err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn); if (err) { printk("Create connection failed (err %d)\n", err); start_scan(); } }else{ //printk("CMP failed\n"); } } static void start_scan(void) { int err; /* Use active scanning and disable duplicate filtering to handle any * devices that might update their advertising data at runtime. */ struct bt_le_scan_param scan_param = { .type = BT_LE_SCAN_TYPE_ACTIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = BT_GAP_SCAN_FAST_INTERVAL, .window = BT_GAP_SCAN_FAST_WINDOW, }; err = bt_le_scan_start(&scan_param, device_found); if (err) { printk("Scanning failed| Unable to load scan parameters\n"); return; } printk("Scanning Start Sucessfull\n"); } static void connected(struct bt_conn *conn, uint8_t conn_err) { int err; char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (conn_err) { printk("Failed to connect to %s (%u)\n", addr, conn_err); bt_conn_unref(default_conn); default_conn = NULL; start_scan(); return; } printk("Connected: %s\n",addr); if (conn == default_conn) { memcpy(&uuid, BT_UUID_TEMPERATURE_ALERT_SERVICE , sizeof(uuid)); discover_params.uuid = &uuid.uuid; discover_params.func = discover_func; discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; discover_params.type = BT_GATT_DISCOVER_PRIMARY; err = bt_gatt_discover(default_conn, &discover_params); if (err) { printk("Discover failed(err %d)\n", err); return; } } } static void disconnected(struct bt_conn *conn, uint8_t reason) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); if (default_conn != conn) { return; } bt_conn_unref(default_conn); default_conn = NULL; start_scan(); } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, }; void main(void) { int err; err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } printk("Bluetooth initialized\n"); start_scan(); } #endif
*** Booting Zephyr OS build v3.1.99-ncs1-1 *** init main [Bluetooth initialized Scanning successfully started 00:00:00.005,493] <inf> sdc_hci_driver: SoftDevice Controller build revision: 29 5c 92 f1 36 81 92 d1 b7 a9 f0 f1 99 e9 4c 19 |)\..6... ......L. 1f 23 83 4a |.#.J [00:00:00.008,911] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002) [00:00:00.008,941] <inf> bt_hci_core: HW Variant: nRF52x (0x0002) [00:00:00.008,972] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 41.37468 Build 2457941745 [00:00:00.010,314] <inf> bt_hci_core: Identity: F0:CF:29:9D:9B:96 (random) [00:00:00.010,314] <inf> bt_hci_core: HCI: version 5.3 (0x0c) revision 0x11d8, manufacturer 0x0059 [00:00:00.010,345] <inf> bt_hci_core: LMP: version 5.3 (0x0c) subver 0x11d8 [DEVICE]: E4:38:55:AE:4D:EE (random), AD evt type 0, AD data len 26, RSSI -55 [AD]: 25 data_len 2 [AD]: 1 data_len 1 [AD]: 3 data_len 6 Connected: E4:38:55:AE:4D:EE (random) Battery [ATTRIBUTE] handle 13 Battery characteristics [SUBSCRIBED] temperature [ATTRIBUTE] handle 38 Battery notify temperature [ATTRIBUTE] handle 39 temperature [ATTRIBUTE] handle 41 temperature measurement [SUBSCRIBED] Temperature notify position [ATTRIBUTE] handle 42 position [ATTRIBUTE] handle 43 position [ATTRIBUTE] handle 45 position characteristics [SUBSCRIBED] Position notify alert [ATTRIBUTE] handle 9 alert [ATTRIBUTE] handle 10 alert [ATTRIBUTE] handle 12 Alert characteristics [SUBSCRIBED] Alert notify fev alert [ATTRIBUTE] handle 17 fev alert [ATTRIBUTE] handle 18 bt write start Write sucess fev alert [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 21 before write CTS length a e6 7 c 1d c 1 0 1 0 0 after Write CTS sucess length a e6 7 c 1d c 1 0 1 0 0 Disconnected Disconnected Disconnected Disconnected Disconnected Disconnected Connected via Serial Port with settings /dev/ttyACM0 115200 8n1 rtscts:off temperature [ATTRIBUTE] handle 38 Battery notify temperature [ATTRIBUTE] handle 39 temperature [ATTRIBUTE] handle 41 temperature measurement [SUBSCRIBED] Temperature notify position [ATTRIBUTE] handle 42 position [ATTRIBUTE] handle 43 position [ATTRIBUTE] handle 45 position characteristics [SUBSCRIBED] Position notify alert [ATTRIBUTE] handle 9 alert [ATTRIBUTE] handle 10 alert [ATTRIBUTE] handle 12 Alert characteristics [SUBSCRIBED] Alert notify fev alert [ATTRIBUTE] handle 17 fev alert [ATTRIBUTE] handle 18 bt write start Write sucess fev alert [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 21 before write CTS length a e6 7 c 1f 7 d 21 1 0 0 after Write CTS sucess length a e6 7 c 1d c 1 0 1 0 0 *** Booting Zephyr OS build v3.1.99-ncs1-1 *** init main [Bluetooth initialized Scanning successfully started 00:00:00.005,462] <inf> sdc_hci_driver: SoftDevice Controller build revision: 29 5c 92 f1 36 81 92 d1 b7 a9 f0 f1 99 e9 4c 19 |)\..6... ......L. 1f 23 83 4a |.#.J [00:00:00.008,850] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002) [00:00:00.008,911] <inf> bt_hci_core: HW Variant: nRF52x (0x0002) [00:00:00.008,911] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 41.37468 Build 2457941745 [00:00:00.010,253] <inf> bt_hci_core: Identity: F0:CF:29:9D:9B:96 (random) [00:00:00.010,284] <inf> bt_hci_core: HCI: version 5.3 (0x0c) revision 0x11d8, manufacturer 0x0059 [00:00:00.010,314] <inf> bt_hci_core: LMP: version 5.3 (0x0c) subver 0x11d8 [DEVICE]: E4:38:55:AE:4D:EE (random), AD evt type 0, AD data len 26, RSSI -49 [AD]: 25 data_len 2 [AD]: 1 data_len 1 [AD]: 3 data_len 6 Connected: E4:38:55:AE:4D:EE (random) Battery [ATTRIBUTE] handle 13 Battery characteristics [SUBSCRIBED] temperature [ATTRIBUTE] handle 38 Battery notify temperature [ATTRIBUTE] handle 39 temperature [ATTRIBUTE] handle 41 temperature measurement [SUBSCRIBED] Temperature notify position [ATTRIBUTE] handle 42 position [ATTRIBUTE] handle 43 position [ATTRIBUTE] handle 45 position characteristics [SUBSCRIBED] Position notify alert [ATTRIBUTE] handle 9 alert [ATTRIBUTE] handle 10 alert [ATTRIBUTE] handle 12 Alert characteristics [SUBSCRIBED] Alert notify fev alert [ATTRIBUTE] handle 17 fev alert [ATTRIBUTE] handle 18 bt write start Write sucess fev alert [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 20 CTS [ATTRIBUTE] handle 21 before write CTS length a e6 7 c 1d c 1 0 1 0 0 after Write CTS sucess length a e6 7 c 1d c 1 0 1 0 0