#include #include #include #include #include #include #include #include #include #include "ble_mgmt.h" LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL); #define BUZZ_SERVICE_UUID_VAL \ BT_UUID_128_ENCODE(0xe517d988, 0xbab5, 0x4574, 0x8479, 0x97c6cb115ca0) #define BUZZ_RX_UUID_VAL \ BT_UUID_128_ENCODE(0xe517d988, 0xbab5, 0x4574, 0x8479, 0x97c6cb115ca1) #define BUZZ_TX_UUID_VAL \ BT_UUID_128_ENCODE(0xe517d988, 0xbab5, 0x4574, 0x8479, 0x97c6cb115ca2) static struct bt_uuid_128 buzz_service_uuid = BT_UUID_INIT_128(BUZZ_SERVICE_UUID_VAL); static struct bt_uuid_128 buzz_rx_uuid = BT_UUID_INIT_128(BUZZ_RX_UUID_VAL); static struct bt_uuid_128 buzz_tx_uuid = BT_UUID_INIT_128(BUZZ_TX_UUID_VAL); static ble_mgmt_rx_cb_t app_rx_cb = NULL; static bool notify_enabled = false; static uint16_t current_tx_mtu = 23; static uint16_t current_rx_mtu = 23; #define MAX_ADV_NAME_LEN 29 static char current_device_name[MAX_ADV_NAME_LEN + 1]; static const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID128_ALL, BUZZ_SERVICE_UUID_VAL), }; static struct bt_data sd[] = { BT_DATA(BT_DATA_NAME_COMPLETE, current_device_name, 0), }; static struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .sid = 0, .secondary_max_skip = 0, .options = BT_LE_ADV_OPT_CONN, .interval_min = CONFIG_BLE_MGMT_ADV_INT_MIN, .interval_max = CONFIG_BLE_MGMT_ADV_INT_MAX, .peer = NULL, }; static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) { LOG_INF("MTU exchanged: TX %u bytes, RX %u bytes", tx, rx); current_tx_mtu = tx; current_rx_mtu = rx; } static ssize_t rx_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { LOG_DBG("Received %u bytes", len); LOG_HEXDUMP_DBG(buf, len, "Data:"); if (app_rx_cb) { app_rx_cb((const uint8_t *)buf, len); } return len; } static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { notify_enabled = (value == BT_GATT_CCC_NOTIFY); LOG_INF("Notifications %s", notify_enabled ? "enabled" : "disabled"); } BT_GATT_SERVICE_DEFINE(ble_mgmt_svc, BT_GATT_PRIMARY_SERVICE(&buzz_service_uuid), BT_GATT_CHARACTERISTIC(&buzz_rx_uuid.uuid, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, rx_cb, NULL), BT_GATT_CHARACTERISTIC(&buzz_tx_uuid.uuid, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_NONE, NULL, NULL, NULL), BT_GATT_CCC(tx_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)); uint16_t ble_mgmt_get_max_payload(void) { /* Kappe die verhandelte MTU auf die hart konfigurierte Zephyr-Puffergrenze */ uint16_t effective_mtu = MIN(current_tx_mtu, current_rx_mtu); #ifdef CONFIG_BT_L2CAP_TX_MTU if (effective_mtu > CONFIG_BT_L2CAP_TX_MTU) { effective_mtu = CONFIG_BT_L2CAP_TX_MTU; } #endif /* 3 Bytes abziehen für den GATT Notification Overhead */ return (effective_mtu > 3) ? (effective_mtu - 3) : 20; } int ble_mgmt_send(const uint8_t *data, uint16_t len) { if (!notify_enabled) { return -EACCES; } int rc; do { rc = bt_gatt_notify(NULL, &ble_mgmt_svc.attrs[4], data, len); if (rc == -ENOMEM) { k_sleep(K_MSEC(5)); // Thread pausieren, bis TX-Buffer frei wird } } while (rc == -ENOMEM); if (rc) { LOG_ERR("Failed to send notification (err %d)", rc); return rc; } return rc; } /* Interne Hilfsfunktion zur Zuweisung des Namens */ static void set_device_name(const char *name) { if (!name) { return; } strncpy(current_device_name, name, MAX_ADV_NAME_LEN); current_device_name[MAX_ADV_NAME_LEN] = '\0'; /* Längen-Update im Scan-Response Array */ sd[0].data_len = strlen(current_device_name); #ifdef CONFIG_BT_DEVICE_NAME_DYNAMIC /* Setzt den Namen parallel im Zephyr GAP-Service (wichtig für macOS) */ bt_set_name(current_device_name); #endif } int ble_mgmt_update_adv_name(const char *new_name) { int rc; bt_le_adv_stop(); set_device_name(new_name); rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (rc) { LOG_ERR("Advertising failed to restart after name update (err %d)", rc); return rc; } LOG_INF("Advertising updated. New Name: %s", current_device_name); return 0; } static void connected(struct bt_conn *conn, uint8_t err) { if (err) { LOG_ERR("Connection failed (err 0x%02x)", err); return; } char addr_str[BT_ADDR_LE_STR_LEN]; struct bt_conn_info info; int rc = bt_conn_get_info(conn, &info); if (rc == 0) { bt_addr_le_to_str(info.le.dst, addr_str, sizeof(addr_str)); LOG_INF("Connected to %s", addr_str); LOG_INF("Role: %s", info.role == BT_CONN_ROLE_CENTRAL ? "Central" : "Peripheral"); } else { LOG_INF("Connected (info retrieval failed)"); } } static void disconnected(struct bt_conn *conn, uint8_t reason) { LOG_INF("Disconnected (reason 0x%02x)", reason); /* Startet Advertising mit dem global definierten Setup neu */ int rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (rc) { LOG_ERR("Advertising failed to restart (err %d)", rc); } else { LOG_INF("Advertising successfully restarted"); } } static void le_phy_updated(struct bt_conn *conn, struct bt_conn_le_phy_info *param) { const char *tx_phy_str = (param->tx_phy == BT_GAP_LE_PHY_2M) ? "2M" : (param->tx_phy == BT_GAP_LE_PHY_1M) ? "1M" : "Coded/Unknown"; const char *rx_phy_str = (param->rx_phy == BT_GAP_LE_PHY_2M) ? "2M" : (param->rx_phy == BT_GAP_LE_PHY_1M) ? "1M" : "Coded/Unknown"; LOG_INF("LE PHY updated: TX PHY %s, RX PHY %s", tx_phy_str, rx_phy_str); } static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { LOG_INF("Connection parameters updated: Interval: %u, Latency: %u, Timeout: %u", interval, latency, timeout); } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, .le_param_updated = le_param_updated, .le_phy_updated = le_phy_updated, }; int ble_mgmt_init(ble_mgmt_rx_cb_t rx_cb, const char *device_name) { int rc; app_rx_cb = rx_cb; static struct bt_gatt_cb gatt_callbacks = { .att_mtu_updated = att_mtu_updated, }; bt_gatt_cb_register(&gatt_callbacks); rc = bt_enable(NULL); if (rc) { LOG_ERR("Bluetooth init failed (err %d)", rc); return rc; } const char *name_to_use = (device_name != NULL) ? device_name : CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME; set_device_name(name_to_use); rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (rc) { LOG_ERR("Advertising failed to start (err %d)", rc); return rc; } LOG_INF("Bluetooth initialized. Adv-Name: %s", current_device_name); return 0; }