diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index eff0ff6..24b84dc 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -9,4 +9,5 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(app LANGUAGES C) -target_sources(app PRIVATE src/main.c) \ No newline at end of file +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) \ No newline at end of file diff --git a/data/main.c b/data/main.c index aff2071..7fbe9b0 100644 --- a/data/main.c +++ b/data/main.c @@ -6,9 +6,6 @@ #include #include -#include -LOG_MODULE_REGISTER(main, CONFIG_APP_LOG_LEVEL); - int main(void) { printk("Zephyr Example Application %s\n", APP_VERSION_STRING); diff --git a/script.sh b/script.sh index b7458e3..884d76a 100755 --- a/script.sh +++ b/script.sh @@ -124,7 +124,18 @@ connectivity_dialog() { # TODO ;; 2 ) # Bluetooth - # TODO + sed -i '/int main(void)/e cat snippets/ble_gatt/code1.txt' /workdir/app/src/main.c + + code2=$(cat snippets/ble_gatt/code2.txt) + echo "$code2" >> /workdir/app/src/peripheral_gatt_write.c + + code3=$(cat snippets/ble_gatt/code3.txt) + echo "$code3" >> /workdir/app/src/gatt_write_common.c + + config=$(cat snippets/ble_gatt/config.txt) + echo "$config" >> /workdir/app/prj.conf + + display_result 'Added functionality' "Added functionality successfully at the end of main.c.\nYou have to call the function like in the example manually." ;; 3 ) # LoRaWAN # TODO @@ -188,12 +199,11 @@ sensors_dialog() { case $sensors in 1 ) # Button - code=$(cat snippets/button/code.txt) - echo "$code" >> /workdir/app/src/main.c + sed -i '/int main(void)/e cat snippets/button/code.txt' /workdir/app/src/main.c config=$(cat snippets/button/config.txt) echo "$config" >> /workdir/app/prj.conf - + display_result 'Added functionality' "Added functionality successfully at the end of main.c.\nYou have to call the function like in the example manually." ;; 2 ) @@ -217,12 +227,11 @@ actuators_dialog() { case $actuators in 1 ) # LED - code=$(cat snippets/led/code.txt) - echo "$code" >> /workdir/app/src/main.c - + sed -i '/int main(void)/e cat snippets/led/code.txt' /workdir/app/src/main.c + config=$(cat snippets/led/config.txt) echo "$config" >> /workdir/app/prj.conf - + display_result 'Added functionality' "Added functionality successfully at the end of main.c.\nYou have to call the function like in the example manually." ;; 2 ) @@ -257,8 +266,13 @@ init_project() { build_project() { plattform_dialog - - { cd /workdir/app/; west build -b $PLATTFORM; } 2>&1 | dialog --programbox -1 -1 + + clear + + cd /workdir/app/ + west build -b $PLATTFORM + + read -p "Press any key to continue... " -n1 -s } flash_project() { diff --git a/snippets/ble_gatt/code1.txt b/snippets/ble_gatt/code1.txt new file mode 100644 index 0000000..bb28a43 --- /dev/null +++ b/snippets/ble_gatt/code1.txt @@ -0,0 +1,26 @@ + + +/* --------------------------- */ +/* BLE */ +/* --------------------------- */ + +/* Imports / Declarations */ +#include + +/* Functions */ +extern uint32_t peripheral_gatt_write(uint32_t count); + +/* +Example: + +int main(void) +{ + (void)peripheral_gatt_write(0U); + return 0; +} +*/ + +/* --------------------------- */ +/* BLE End */ +/* --------------------------- */ + diff --git a/snippets/ble_gatt/code2.txt b/snippets/ble_gatt/code2.txt new file mode 100644 index 0000000..7dcae85 --- /dev/null +++ b/snippets/ble_gatt/code2.txt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +extern int mtu_exchange(struct bt_conn *conn); +extern int write_cmd(struct bt_conn *conn); +extern struct bt_conn *conn_connected; +extern uint32_t last_write_rate; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), +}; + +static void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) +{ + printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx); +} + +#if defined(CONFIG_BT_SMP) +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing cancelled: %s\n", addr); +} + +static struct bt_conn_auth_cb auth_callbacks = { + .cancel = auth_cancel, +}; +#endif /* CONFIG_BT_SMP */ + +static struct bt_gatt_cb gatt_callbacks = { + .att_mtu_updated = mtu_updated +}; + +uint32_t peripheral_gatt_write(uint32_t count) +{ + int err; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0U; + } + + printk("Bluetooth initialized\n"); + + bt_gatt_cb_register(&gatt_callbacks); + +#if defined(CONFIG_BT_SMP) + (void)bt_conn_auth_cb_register(&auth_callbacks); +#endif /* CONFIG_BT_SMP */ + + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return 0U; + } + + printk("Advertising successfully started\n"); + + conn_connected = NULL; + last_write_rate = 0U; + + while (true) { + struct bt_conn *conn = NULL; + + if (conn_connected) { + /* Get a connection reference to ensure that a + * reference is maintained in case disconnected + * callback is called while we perform GATT Write + * command. + */ + conn = bt_conn_ref(conn_connected); + } + + if (conn) { + write_cmd(conn); + bt_conn_unref(conn); + + if (count) { + count--; + if (!count) { + break; + } + } + + k_yield(); + } else { + k_sleep(K_SECONDS(1)); + } + } + + return last_write_rate; +} diff --git a/snippets/ble_gatt/code3.txt b/snippets/ble_gatt/code3.txt new file mode 100644 index 0000000..58c8c58 --- /dev/null +++ b/snippets/ble_gatt/code3.txt @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +static struct bt_gatt_exchange_params mtu_exchange_params; +static uint32_t write_count; +static uint32_t write_len; +static uint32_t write_rate; +struct bt_conn *conn_connected; +uint32_t last_write_rate; +void (*start_scan_func)(void); + +static void write_cmd_cb(struct bt_conn *conn, void *user_data) +{ + static uint32_t cycle_stamp; + uint64_t delta; + + delta = k_cycle_get_32() - cycle_stamp; + delta = k_cyc_to_ns_floor64(delta); + + /* if last data rx-ed was greater than 1 second in the past, + * reset the metrics. + */ + if (delta > (1U * NSEC_PER_SEC)) { + printk("%s: count= %u, len= %u, rate= %u bps.\n", __func__, + write_count, write_len, write_rate); + + last_write_rate = write_rate; + + write_count = 0U; + write_len = 0U; + write_rate = 0U; + cycle_stamp = k_cycle_get_32(); + } else { + uint16_t len; + + write_count++; + + /* Extract the 16-bit data length stored in user_data */ + len = (uint32_t)user_data & 0xFFFF; + + write_len += len; + write_rate = ((uint64_t)write_len << 3) * (1U * NSEC_PER_SEC) / + delta; + } +} + +static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_exchange_params *params) +{ + printk("%s: MTU exchange %s (%u)\n", __func__, + err == 0U ? "successful" : "failed", + bt_gatt_get_mtu(conn)); +} + +static int mtu_exchange(struct bt_conn *conn) +{ + int err; + + printk("%s: Current MTU = %u\n", __func__, bt_gatt_get_mtu(conn)); + + mtu_exchange_params.func = mtu_exchange_cb; + + printk("%s: Exchange MTU...\n", __func__); + err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params); + if (err) { + printk("%s: MTU exchange failed (err %d)", __func__, err); + } + + return err; +} + +static void connected(struct bt_conn *conn, uint8_t conn_err) +{ + struct bt_conn_info conn_info; + 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("%s: Failed to connect to %s (%u)\n", __func__, addr, + conn_err); + return; + } + + err = bt_conn_get_info(conn, &conn_info); + if (err) { + printk("Failed to get connection info (%d).\n", err); + return; + } + + printk("%s: %s role %u\n", __func__, addr, conn_info.role); + + conn_connected = bt_conn_ref(conn); + + (void)mtu_exchange(conn); + +#if defined(CONFIG_BT_SMP) + if (conn_info.role == BT_CONN_ROLE_CENTRAL) { + err = bt_conn_set_security(conn, BT_SECURITY_L2); + if (err) { + printk("Failed to set security (%d).\n", err); + } + } +#endif +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + struct bt_conn_info conn_info; + char addr[BT_ADDR_LE_STR_LEN]; + int err; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + err = bt_conn_get_info(conn, &conn_info); + if (err) { + printk("Failed to get connection info (%d).\n", err); + return; + } + + printk("%s: %s role %u (reason %u)\n", __func__, addr, conn_info.role, + reason); + + conn_connected = NULL; + + bt_conn_unref(conn); + + if (conn_info.role == BT_CONN_ROLE_CENTRAL) { + start_scan_func(); + } +} + +static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) +{ + printk("%s: int (0x%04x, 0x%04x) lat %u to %u\n", __func__, + param->interval_min, param->interval_max, param->latency, + param->timeout); + + return true; +} + +static void le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) +{ + printk("%s: int 0x%04x lat %u to %u\n", __func__, interval, + latency, timeout); +} + +#if defined(CONFIG_BT_SMP) +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); +} +#endif + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, + .le_param_req = le_param_req, + .le_param_updated = le_param_updated, +#if defined(CONFIG_BT_SMP) + .security_changed = security_changed, +#endif +}; + +int write_cmd(struct bt_conn *conn) +{ + static uint8_t data[BT_ATT_MAX_ATTRIBUTE_LEN] = {0, }; + static uint16_t data_len; + uint16_t data_len_max; + int err; + + data_len_max = bt_gatt_get_mtu(conn) - 3; + if (data_len_max > BT_ATT_MAX_ATTRIBUTE_LEN) { + data_len_max = BT_ATT_MAX_ATTRIBUTE_LEN; + } + +#if TEST_FRAGMENTATION_WITH_VARIABLE_LENGTH_DATA + /* Use incremental length data for every write command */ + /* TODO: Include test case in BabbleSim tests */ + static bool decrement; + + if (decrement) { + data_len--; + if (data_len <= 1) { + data_len = 1; + decrement = false; + } + } else { + data_len++; + if (data_len >= data_len_max) { + data_len = data_len_max; + decrement = true; + } + } +#else + /* Use fixed length data for every write command */ + data_len = data_len_max; +#endif + + /* Pass the 16-bit data length value (instead of reference) in + * user_data so that unique value is pass for each write callback. + * Using handle 0x0001, we do not care if it is writable, we just want + * to transmit the data across. + */ + err = bt_gatt_write_without_response_cb(conn, 0x0001, data, data_len, + false, write_cmd_cb, + (void *)((uint32_t)data_len)); + if (err) { + printk("%s: Write cmd failed (%d).\n", __func__, err); + } + + return err; +} diff --git a/snippets/ble_gatt/config.txt b/snippets/ble_gatt/config.txt new file mode 100644 index 0000000..3cc29c6 --- /dev/null +++ b/snippets/ble_gatt/config.txt @@ -0,0 +1,14 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_SMP=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_DEVICE_NAME="Zephyr GATT Write Example" + +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255 + +CONFIG_BT_L2CAP_TX_MTU=247 + +CONFIG_LOG=y diff --git a/snippets/button/code.txt b/snippets/button/code.txt index 1bc0dac..f4fd2da 100644 --- a/snippets/button/code.txt +++ b/snippets/button/code.txt @@ -113,4 +113,9 @@ int main(void) return 0; } -*/ \ No newline at end of file +*/ + +/* --------------------------- */ +/* Buttons End */ +/* --------------------------- */ + diff --git a/snippets/led/code.txt b/snippets/led/code.txt index 88effe0..9839a6c 100644 --- a/snippets/led/code.txt +++ b/snippets/led/code.txt @@ -55,4 +55,9 @@ int main(void) return 0; } -*/ \ No newline at end of file +*/ + +/* --------------------------- */ +/* LED End */ +/* --------------------------- */ +