text: /* 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.h>
  #include <sys/printk.h>
  #include <sys/byteorder.h>
  
  #include <bluetooth/bluetooth.h>
  #include <bluetooth/hci.h>
  #include <bluetooth/conn.h>
  #include <bluetooth/uuid.h>
  #include <bluetooth/gatt.h>
  
  static int scan_start(void);
  
  static struct bt_conn *default_conn;
  static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
  static struct bt_gatt_discover_params discover_params;
  static struct bt_gatt_subscribe_params subscribe_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(struct bt_conn *conn,
  			   struct bt_gatt_subscribe_params *params,
  			   const void *data, uint16_t length)
  {
  	double temperature;
  	uint32_t mantissa;
  	int8_t exponent;
  
  	if (!data) {
  		printk("[UNSUBSCRIBED]\n");
  		params->value_handle = 0U;
  		return BT_GATT_ITER_STOP;
  	}
  
  	/* 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 discover_func(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("[ATTRIBUTE] handle %u\n", attr->handle);
  
  	if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_HTS)) {
  		memcpy(&uuid, BT_UUID_HTS_MEASUREMENT, 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_HTS_MEASUREMENT)) {
  		memcpy(&uuid, BT_UUID_GATT_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 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);
  
  	if (conn == default_conn) {
  		memcpy(&uuid, BT_UUID_HTS, sizeof(uuid));
  		discover_params.uuid = &uuid.uuid;
  		discover_params.func = discover_func;
  		discover_params.start_handle = BT_ATT_FIRST_ATTTRIBUTE_HANDLE;
  		discover_params.end_handle = BT_ATT_LAST_ATTTRIBUTE_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 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];
  
  	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,
  };
  
  void main(void)
  {
  	int err;
  
  	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");
  }
  