/* * Copyright (c) 2021-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void start_scan(void); static struct bt_conn *default_conn; static struct k_work_delayable iso_send_work; static struct bt_iso_chan iso_chan; static uint16_t seq_num; static uint16_t latency_ms = 10U; /* 10ms */ static uint32_t interval_us = 10U * USEC_PER_MSEC; /* 10 ms */ NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); /** * @brief Send ISO data on timeout * * This will send an increasing amount of ISO data, starting from 1 octet. * * First iteration : 0x00 * Second iteration: 0x00 0x01 * Third iteration : 0x00 0x01 0x02 * * And so on, until it wraps around the configured ISO TX MTU (CONFIG_BT_ISO_TX_MTU) * * @param work Pointer to the work structure */ static void iso_timer_timeout(struct k_work *work) { int ret; static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU]; static bool data_initialized; struct net_buf *buf; static size_t len_to_send = 1; if (!data_initialized) { for (int i = 0; i < ARRAY_SIZE(buf_data); i++) { buf_data[i] = (uint8_t)i; } data_initialized = true; } buf = net_buf_alloc(&tx_pool, K_NO_WAIT); if (buf != NULL) { net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); net_buf_add_mem(buf, buf_data, len_to_send); ret = bt_iso_chan_send(&iso_chan, buf, seq_num); if (ret < 0) { printk("Failed to send ISO data (%d)\n", ret); net_buf_unref(buf); } len_to_send++; if (len_to_send > ARRAY_SIZE(buf_data)) { len_to_send = 1; } } else { printk("Failed to allocate buffer, retrying in next interval (%u us)\n", interval_us); } /* Sequence number shall be incremented for each SDU interval */ seq_num++; k_work_schedule(&iso_send_work, K_USEC(interval_us)); } static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { const struct bt_le_conn_param *conn_param = BT_LE_CONN_PARAM(BT_GAP_MS_TO_CONN_INTERVAL(60), BT_GAP_MS_TO_CONN_INTERVAL(60), 0, BT_GAP_MS_TO_CONN_TIMEOUT(4000)); char addr_str[BT_ADDR_LE_STR_LEN]; int err; if (default_conn) { /* Already connected */ return; } /* We're only interested in connectable events */ if (type != BT_GAP_ADV_TYPE_ADV_IND && type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { return; } bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); printk("Device found: %s (RSSI %d)\n", addr_str, rssi); /* connect only to devices in close proximity */ if (rssi < -50) { return; } if (bt_le_scan_stop()) { return; } err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, conn_param, &default_conn); if (err) { printk("Create conn to %s failed (%u)\n", addr_str, err); start_scan(); } } static void start_scan(void) { int err; /* This demo doesn't require active scan */ err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); if (err) { printk("Scanning failed to start (err %d)\n", err); return; } printk("Scanning successfully started\n"); } static void iso_connected(struct bt_iso_chan *chan) { printk("ISO Channel %p connected\n", chan); seq_num = 0U; /* Start send timer */ k_work_schedule(&iso_send_work, K_MSEC(0)); } static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) { printk("ISO Channel %p disconnected (reason 0x%02x)\n", chan, reason); k_work_cancel_delayable(&iso_send_work); } static struct bt_iso_chan_ops iso_ops = { .connected = iso_connected, .disconnected = iso_disconnected, }; static struct bt_iso_chan_io_qos iso_tx = { .sdu = CONFIG_BT_ISO_TX_MTU, .phy = BT_GAP_LE_PHY_2M, .rtn = 1, .path = NULL, }; static struct bt_iso_chan_qos iso_qos = { .tx = &iso_tx, .rx = NULL, }; static void connected(struct bt_conn *conn, uint8_t err) { char addr[BT_ADDR_LE_STR_LEN]; int iso_err; struct bt_iso_connect_param connect_param; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (err) { printk("Failed to connect to %s %u %s\n", addr, err, bt_hci_err_to_str(err)); bt_conn_unref(default_conn); default_conn = NULL; start_scan(); return; } if (conn != default_conn) { return; } printk("Connected: %s\n", addr); connect_param.acl = conn; connect_param.iso_chan = &iso_chan; iso_err = bt_iso_chan_connect(&connect_param, 1); if (iso_err) { printk("Failed to connect iso (%d)\n", iso_err); return; } } static void disconnected(struct bt_conn *conn, uint8_t reason) { char addr[BT_ADDR_LE_STR_LEN]; if (conn != default_conn) { return; } bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason)); bt_conn_unref(default_conn); default_conn = NULL; start_scan(); } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, }; int main(void) { int err; struct bt_iso_chan *channels[1]; struct bt_iso_cig_param param; struct bt_iso_cig *cig; err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return 0; } if (IS_ENABLED(CONFIG_SETTINGS)) { settings_load(); } printk("Bluetooth initialized\n"); iso_chan.ops = &iso_ops; iso_chan.qos = &iso_qos; #if defined(CONFIG_BT_SMP) iso_chan.required_sec_level = BT_SECURITY_L2, #endif /* CONFIG_BT_SMP */ channels[0] = &iso_chan; param.cis_channels = channels; param.num_cis = ARRAY_SIZE(channels); param.sca = BT_GAP_SCA_UNKNOWN; param.packing = 0; param.framing = 0; param.c_to_p_latency = latency_ms; /* ms */ param.p_to_c_latency = latency_ms; /* ms */ param.c_to_p_interval = interval_us; /* us */ param.p_to_c_interval = interval_us; /* us */ err = bt_iso_cig_create(¶m, &cig); if (err != 0) { printk("Failed to create CIG (%d)\n", err); return 0; } start_scan(); k_work_init_delayable(&iso_send_work, iso_timer_timeout); return 0; }