diff --git a/firmware/libs/ble_mgmt/Kconfig b/firmware/libs/ble_mgmt/Kconfig index 90c074b..28bbd30 100644 --- a/firmware/libs/ble_mgmt/Kconfig +++ b/firmware/libs/ble_mgmt/Kconfig @@ -28,34 +28,34 @@ if BLE_MGMT help Maximal advertising interval. 160 equals to 100ms. - config BT_L2CAP_TX_MTU - default 247 - config BT_BUF_ACL_RX_SIZE - default 251 - config BT_BUF_ACL_TX_SIZE - default 251 - config BT_CTLR_DATA_LENGTH_MAX - default 251 - config BT_USER_DATA_LEN_UPDATE - default y - config BT_USER_PHY_UPDATE - default y - config BT_HCI_ACL_FLOW_CONTROL - default y - config BT_BUF_CMD_TX_COUNT - default 24 - config BT_BUF_EVT_RX_COUNT - default 22 - config BT_BUF_ACL_TX_COUNT - default 20 - config BT_L2CAP_TX_BUF_COUNT - default 20 - config BT_CONN_TX_MAX - default 20 - config BT_CTLR_SDC_TX_PACKET_COUNT - default 20 - config BT_CTLR_SDC_RX_PACKET_COUNT - default 20 + # config BT_L2CAP_TX_MTU + # default 247 + # config BT_BUF_ACL_RX_SIZE + # default 251 + # config BT_BUF_ACL_TX_SIZE + # default 251 + # config BT_CTLR_DATA_LENGTH_MAX + # default 251 + # config BT_USER_DATA_LEN_UPDATE + # default y + # config BT_USER_PHY_UPDATE + # default y + # config BT_HCI_ACL_FLOW_CONTROL + # default y + # config BT_BUF_CMD_TX_COUNT + # default 24 + # config BT_BUF_EVT_RX_COUNT + # default 22 + # config BT_BUF_ACL_TX_COUNT + # default 20 + # config BT_L2CAP_TX_BUF_COUNT + # default 20 + # config BT_CONN_TX_MAX + # default 20 + # config BT_CTLR_SDC_TX_PACKET_COUNT + # default 20 + # config BT_CTLR_SDC_RX_PACKET_COUNT + # default 20 config BT_MAX_CONN default 2 diff --git a/firmware/libs/ble_mgmt/src/ble_mgmt.c b/firmware/libs/ble_mgmt/src/ble_mgmt.c index bad33c1..5208421 100644 --- a/firmware/libs/ble_mgmt/src/ble_mgmt.c +++ b/firmware/libs/ble_mgmt/src/ble_mgmt.c @@ -5,6 +5,8 @@ #include #include #include +#include + #include #include "ble_mgmt.h" @@ -24,7 +26,9 @@ 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]; @@ -52,6 +56,7 @@ 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, @@ -59,8 +64,9 @@ static ssize_t rx_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, { LOG_DBG("Received %u bytes", len); LOG_HEXDUMP_DBG(buf, len, "Data:"); - - if (app_rx_cb) { + + if (app_rx_cb) + { app_rx_cb((const uint8_t *)buf, len); } return len; @@ -69,25 +75,25 @@ static ssize_t rx_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { notify_enabled = (value == BT_GATT_CCC_NOTIFY); - LOG_DBG("Notifications %s", notify_enabled ? "enabled" : "disabled"); + 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) -); + 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 = current_tx_mtu; - + 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) { + if (effective_mtu > CONFIG_BT_L2CAP_TX_MTU) + { effective_mtu = CONFIG_BT_L2CAP_TX_MTU; } #endif @@ -98,19 +104,23 @@ uint16_t ble_mgmt_get_max_payload(void) int ble_mgmt_send(const uint8_t *data, uint16_t len) { - if (!notify_enabled) { + if (!notify_enabled) + { return -EACCES; } int rc; - do { + do + { rc = bt_gatt_notify(NULL, &ble_mgmt_svc.attrs[4], data, len); - if (rc == -ENOMEM) { + if (rc == -ENOMEM) + { k_sleep(K_MSEC(5)); // Thread pausieren, bis TX-Buffer frei wird } } while (rc == -ENOMEM); - if (rc) { + if (rc) + { LOG_ERR("Failed to send notification (err %d)", rc); return rc; } @@ -120,13 +130,14 @@ int ble_mgmt_send(const uint8_t *data, uint16_t len) /* Interne Hilfsfunktion zur Zuweisung des Namens */ static void set_device_name(const char *name) { - if (!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); @@ -144,7 +155,8 @@ int ble_mgmt_update_adv_name(const char *new_name) set_device_name(new_name); rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); - if (rc) { + if (rc) + { LOG_ERR("Advertising failed to restart after name update (err %d)", rc); return rc; } @@ -153,40 +165,10 @@ int ble_mgmt_update_adv_name(const char *new_name) return 0; } -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; -} - static void connected(struct bt_conn *conn, uint8_t err) { - if (err) { + if (err) + { LOG_ERR("Connection failed (err 0x%02x)", err); return; } @@ -195,50 +177,38 @@ static void connected(struct bt_conn *conn, uint8_t err) struct bt_conn_info info; int rc = bt_conn_get_info(conn, &info); - if (rc == 0) { + if (rc == 0) + { bt_addr_le_to_str(info.le.dst, addr_str, sizeof(addr_str)); LOG_INF("Connected to %s", addr_str); - - /* Nur noch die Rolle ausgeben, da Timing-Parameter hier deprecated sind */ - LOG_DBG("Role: %s", info.role == BT_CONN_ROLE_CENTRAL ? "Central" : "Peripheral"); - } else { + LOG_INF("Role: %s", info.role == BT_CONN_ROLE_CENTRAL ? "Central" : "Peripheral"); + } + else + { LOG_INF("Connected (info retrieval failed)"); } - struct bt_conn_le_phy_param phy_param = { - .options = BT_CONN_LE_PHY_OPT_NONE, - .pref_tx_phy = BT_GAP_LE_PHY_2M, - .pref_rx_phy = BT_GAP_LE_PHY_2M, - }; - rc = bt_conn_le_phy_update(conn, &phy_param); - if (rc) { - LOG_WRN("PHY update failed (err %d)", rc); - } - struct bt_le_conn_param *param = BT_LE_CONN_PARAM(12, 24, 0, 400); - rc = bt_conn_le_param_update(conn, param); - if (rc) { - LOG_WRN("Connection update failed (err %d)", rc); - } } static void disconnected(struct bt_conn *conn, uint8_t reason) { - LOG_DBG("Disconnected (reason 0x%02x)", 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) { + if (rc) + { LOG_ERR("Advertising failed to restart (err %d)", rc); - } else { - LOG_DBG("Advertising successfully restarted"); + } + 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"; + 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); } @@ -256,3 +226,35 @@ BT_CONN_CB_DEFINE(conn_callbacks) = { .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; +} \ No newline at end of file diff --git a/firmware/libs/buzz_proto/Kconfig b/firmware/libs/buzz_proto/Kconfig index 7d8e38b..166c264 100644 --- a/firmware/libs/buzz_proto/Kconfig +++ b/firmware/libs/buzz_proto/Kconfig @@ -6,7 +6,7 @@ menuconfig BUZZ_PROTO config BUZZ_PROTO_SLAB_SIZE int "Slab Size" - default 256 + default 512 help Size of the memory slabs used for message buffers. Must be large enough to hold the largest expected message. @@ -18,7 +18,7 @@ menuconfig BUZZ_PROTO config BUZZ_PROTO_MSGQ_SIZE int "Message Queue Size" - default 16 + default 64 help Number of messages that can be queued for processing. Adjust based on expected message burstiness. diff --git a/firmware/libs/fs_mgmt/src/fs_mgmt.c b/firmware/libs/fs_mgmt/src/fs_mgmt.c index a3d6452..f92cb64 100644 --- a/firmware/libs/fs_mgmt/src/fs_mgmt.c +++ b/firmware/libs/fs_mgmt/src/fs_mgmt.c @@ -29,7 +29,9 @@ static struct fs_mount_t fs_storage_mnt = { static int open_count = 0; static struct k_mutex flash_pm_lock; -#define ACK_WATERMARK (CONFIG_BUZZ_PROTO_SLAB_COUNT / 4) +// #define ACK_WATERMARK (CONFIG_BUZZ_PROTO_SLAB_COUNT / 4) +#define INITIAL_CREDITS CONFIG_BUZZ_PROTO_SLAB_COUNT +#define ACK_WATERMARK (MAX(2, MIN(8, CONFIG_BUZZ_PROTO_SLAB_COUNT / 4))) typedef struct __attribute__((packed)) { @@ -40,7 +42,8 @@ typedef struct __attribute__((packed)) K_MSGQ_DEFINE(fs_write_msgq, sizeof(struct fs_write_msg), CONFIG_BUZZ_PROTO_SLAB_COUNT, 4); -typedef enum { +typedef enum +{ FS_STATE_IDLE, FS_STATE_RECEIVING_FILE, FS_STATE_RECEIVING_TAGS, @@ -509,7 +512,8 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) if (rc == -EAGAIN) { LOG_WRN("Write timeout! Aborting transfer."); - if (write_ctx.state == FS_STATE_RECEIVING_FILE) { + if (write_ctx.state == FS_STATE_RECEIVING_FILE) + { fs_mgmt_pm_close(&write_ctx.file); fs_mgmt_pm_unlink(write_ctx.filename); } @@ -522,49 +526,56 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) case FS_STATE_IDLE: if (msg.op == FS_WRITE_OP_FILE_START) { - if (msg.data_len >= sizeof(write_ctx.filename)) { + if (msg.data_len >= sizeof(write_ctx.filename)) + { LOG_ERR("Filename too long"); buzz_proto_send_error_reusing_slab(msg.reply_cb, ENAMETOOLONG, msg.slab_ptr); break; } - + memcpy(write_ctx.filename, msg.slab_ptr + msg.data_offset, msg.data_len); write_ctx.filename[msg.data_len] = '\0'; - - fs_mgmt_pm_unlink(write_ctx.filename); + + fs_mgmt_pm_unlink(write_ctx.filename); rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_CREATE | FS_O_WRITE); - - if (rc == 0) { + + if (rc == 0) + { write_ctx.state = FS_STATE_RECEIVING_FILE; write_ctx.crc32 = 0; write_ctx.unacked_chunks = 0; LOG_INF("File transfer started: %s (Expected: %u bytes)", write_ctx.filename, msg.metadata); - - uint16_t credits = buzz_proto_get_free_rx_slabs(); + + uint16_t credits = MIN(INITIAL_CREDITS, buzz_proto_get_free_rx_slabs()); buzz_proto_buf_free(&msg.slab_ptr); buzz_proto_send_ack(msg.reply_cb, credits); - } else { + } + else + { LOG_ERR("Failed to open %s: %d", write_ctx.filename, rc); buzz_proto_send_error_reusing_slab(msg.reply_cb, abs(rc), msg.slab_ptr); } - }/* Innerhalb von case FS_STATE_IDLE: */ + } /* Innerhalb von case FS_STATE_IDLE: */ else if (msg.op == FS_WRITE_OP_TAGS_START) { - if (msg.data_len >= sizeof(write_ctx.filename)) { + if (msg.data_len >= sizeof(write_ctx.filename)) + { buzz_proto_send_error_reusing_slab(msg.reply_cb, ENAMETOOLONG, msg.slab_ptr); break; } - + memcpy(write_ctx.filename, msg.slab_ptr + msg.data_offset, msg.data_len); write_ctx.filename[msg.data_len] = '\0'; - + /* Datei öffnen: Nur Lese- und Schreibrechte, Datei muss bereits existieren */ int rc = fs_mgmt_pm_open(&write_ctx.file, write_ctx.filename, FS_O_READ | FS_O_WRITE); - - if (rc == 0) { + + if (rc == 0) + { ssize_t audio_len = fs_get_audio_data_len(&write_ctx.file); - - if (audio_len < 0) { + + if (audio_len < 0) + { LOG_ERR("Failed to get audio length: %d", (int)audio_len); fs_mgmt_pm_close(&write_ctx.file); buzz_proto_send_error_reusing_slab(msg.reply_cb, EIO, msg.slab_ptr); @@ -573,7 +584,8 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) /* Datei ab dem Ende der Audiodaten abschneiden (alte Tags entfernen) */ rc = fs_truncate(&write_ctx.file, audio_len); - if (rc != 0) { + if (rc != 0) + { LOG_ERR("Failed to truncate file: %d", rc); fs_mgmt_pm_close(&write_ctx.file); buzz_proto_send_error_reusing_slab(msg.reply_cb, abs(rc), msg.slab_ptr); @@ -587,17 +599,20 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) write_ctx.crc32 = 0; write_ctx.unacked_chunks = 0; write_ctx.audio_len = audio_len; - + LOG_INF("Tags transfer started: %s (Expected tags: %u bytes)", write_ctx.filename, msg.metadata); - + uint16_t credits = buzz_proto_get_free_rx_slabs(); buzz_proto_buf_free(&msg.slab_ptr); buzz_proto_send_ack(msg.reply_cb, credits); - } else { + } + else + { LOG_ERR("Failed to open %s for tags: %d", write_ctx.filename, rc); buzz_proto_send_error_reusing_slab(msg.reply_cb, abs(rc), msg.slab_ptr); } - } else if ( msg.op == FS_WRITE_OP_FW_START) + } + else if (msg.op == FS_WRITE_OP_FW_START) { LOG_WRN("Operation not yet fully implemented in FS state machine"); buzz_proto_send_error_reusing_slab(msg.reply_cb, ENOSYS, msg.slab_ptr); @@ -608,20 +623,25 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) if (msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) { ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); - - if (written == msg.data_len) { + + if (written == msg.data_len) + { write_ctx.crc32 = crc32_ieee_update(write_ctx.crc32, msg.slab_ptr + msg.data_offset, msg.data_len); buzz_proto_buf_free(&msg.slab_ptr); write_ctx.unacked_chunks++; - if (write_ctx.unacked_chunks >= ACK_WATERMARK) { + if (write_ctx.unacked_chunks >= ACK_WATERMARK) + { uint16_t free_slabs = buzz_proto_get_free_rx_slabs(); uint16_t credits_to_send = MIN(free_slabs, write_ctx.unacked_chunks); - if (credits_to_send > 0) { + if (credits_to_send > 0) + { buzz_proto_send_ack(msg.reply_cb, credits_to_send); write_ctx.unacked_chunks -= credits_to_send; - } + } } - } else { + } + else + { LOG_ERR("Flash write failed!"); write_ctx.state = FS_STATE_IDLE; buzz_proto_send_error_reusing_slab(msg.reply_cb, EIO, msg.slab_ptr); @@ -631,11 +651,14 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) { fs_mgmt_pm_close(&write_ctx.file); write_ctx.state = FS_STATE_IDLE; - - if (write_ctx.crc32 == msg.metadata) { + + if (write_ctx.crc32 == msg.metadata) + { LOG_INF("File transfer finished. CRC valid: 0x%08X", write_ctx.crc32); buzz_proto_send_success_reusing_slab(msg.reply_cb, BUZZ_DATA_FILE_PUT, msg.slab_ptr); - } else { + } + else + { LOG_ERR("CRC Mismatch! Expected: 0x%08X, Got: 0x%08X", msg.metadata, write_ctx.crc32); fs_mgmt_pm_unlink(write_ctx.filename); buzz_proto_send_error_reusing_slab(msg.reply_cb, EBADMSG, msg.slab_ptr); @@ -646,29 +669,35 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) fs_mgmt_pm_close(&write_ctx.file); fs_mgmt_pm_unlink(write_ctx.filename); write_ctx.state = FS_STATE_IDLE; - if (msg.slab_ptr) buzz_proto_buf_free(&msg.slab_ptr); + if (msg.slab_ptr) + buzz_proto_buf_free(&msg.slab_ptr); } break; - - case FS_STATE_RECEIVING_TAGS: + + case FS_STATE_RECEIVING_TAGS: if (msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) { ssize_t written = fs_write(&write_ctx.file, msg.slab_ptr + msg.data_offset, msg.data_len); - - if (written == msg.data_len) { + + if (written == msg.data_len) + { write_ctx.crc32 = crc32_ieee_update(write_ctx.crc32, msg.slab_ptr + msg.data_offset, msg.data_len); buzz_proto_buf_free(&msg.slab_ptr); - + write_ctx.unacked_chunks++; - if (write_ctx.unacked_chunks >= ACK_WATERMARK) { + if (write_ctx.unacked_chunks >= ACK_WATERMARK) + { uint16_t free_slabs = buzz_proto_get_free_rx_slabs(); uint16_t credits_to_send = MIN(free_slabs, write_ctx.unacked_chunks); - if (credits_to_send > 0) { + if (credits_to_send > 0) + { buzz_proto_send_ack(msg.reply_cb, credits_to_send); write_ctx.unacked_chunks -= credits_to_send; - } + } } - } else { + } + else + { LOG_ERR("Flash write failed during tags transfer!"); fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ fs_mgmt_pm_close(&write_ctx.file); @@ -678,11 +707,14 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) } else if (msg.op == FS_WRITE_OP_FILE_END) { - if (write_ctx.crc32 == msg.metadata) { + if (write_ctx.crc32 == msg.metadata) + { LOG_INF("Tags transfer finished. CRC valid: 0x%08X", write_ctx.crc32); fs_mgmt_pm_close(&write_ctx.file); buzz_proto_send_success_reusing_slab(msg.reply_cb, BUZZ_DATA_TAGS_PUT, msg.slab_ptr); - } else { + } + else + { LOG_ERR("Tags CRC Mismatch! Expected: 0x%08X, Got: 0x%08X", msg.metadata, write_ctx.crc32); fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ fs_mgmt_pm_close(&write_ctx.file); @@ -695,20 +727,22 @@ static void fs_thread_entry(void *p1, void *p2, void *p3) fs_truncate(&write_ctx.file, write_ctx.audio_len); /* Rollback */ fs_mgmt_pm_close(&write_ctx.file); write_ctx.state = FS_STATE_IDLE; - if (msg.slab_ptr) buzz_proto_buf_free(&msg.slab_ptr); + if (msg.slab_ptr) + buzz_proto_buf_free(&msg.slab_ptr); } break; - + case FS_STATE_RECEIVING_FIRMWARE: break; } /* Garbage Collection: Ungültige Operationen im falschen State abfangen */ - if (write_ctx.state == FS_STATE_IDLE && msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) { + if (write_ctx.state == FS_STATE_IDLE && msg.op == FS_WRITE_OP_FILE_CHUNK && msg.slab_ptr) + { buzz_proto_buf_free(&msg.slab_ptr); } } } -K_THREAD_DEFINE(fs_thread, CONFIG_FS_MGMT_THREAD_STACK_SIZE, fs_thread_entry, +K_THREAD_DEFINE(fs_thread, CONFIG_FS_MGMT_THREAD_STACK_SIZE, fs_thread_entry, NULL, NULL, NULL, CONFIG_FS_MGMT_THREAD_PRIORITY, 0, 0); \ No newline at end of file diff --git a/firmware/prj.conf b/firmware/prj.conf index 620efaf..c2b6b6e 100644 --- a/firmware/prj.conf +++ b/firmware/prj.conf @@ -8,7 +8,6 @@ CONFIG_FS_LOG_LEVEL_WRN=y ### Bluetooth CONFIG_BLE_MGMT=y -# CONFIG_BLE_MGMT_LOG_LEVEL_DBG=y # Advertising 500ms - 1s CONFIG_BLE_MGMT_ADV_INT_MIN=160 @@ -23,4 +22,36 @@ CONFIG_PM_DEVICE=y ## Shell # CONFIG_SHELL=y -# CONFIG_FILE_SYSTEM_SHELL=y \ No newline at end of file +# CONFIG_FILE_SYSTEM_SHELL=y + +# Airtime-Maximierung +CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000 + +# MTU-Setup +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=502 +CONFIG_BT_L2CAP_TX_MTU=498 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +# Puffer-Konfiguration (TX = 15, EVT = 16) +CONFIG_BT_BUF_ACL_TX_COUNT=15 +CONFIG_BT_L2CAP_TX_BUF_COUNT=15 +CONFIG_BT_CONN_TX_MAX=15 +CONFIG_BT_CTLR_SDC_TX_PACKET_COUNT=15 +CONFIG_BT_CTLR_SDC_RX_PACKET_COUNT=15 +CONFIG_BT_BUF_EVT_RX_COUNT=16 + +# WICHTIG: Diese Flags aktivieren die Callbacks in der bt_conn_cb Struktur +CONFIG_BT_USER_PHY_UPDATE=y +CONFIG_BT_USER_DATA_LEN_UPDATE=y + +# Automatische Updates im Hintergrund aktivieren +CONFIG_BT_AUTO_PHY_UPDATE=y +CONFIG_BT_AUTO_DATA_LEN_UPDATE=y +CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=y + +# Bevorzugte Parameter für das Auto-Update definieren (entspricht BT_LE_CONN_PARAM(12, 36, 0, 400)) +CONFIG_BT_PERIPHERAL_PREF_MIN_INT=12 +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=40 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=0 +CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c index 81eb2b5..90c7f6f 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -13,8 +13,8 @@ void ble_rx_cb(const uint8_t *data, uint16_t len) uint8_t *buf; /* 1. Länge prüfen (darf SLAB_BLOCK_SIZE = 256 nicht überschreiten) */ - if (len > 256) { - LOG_ERR("Received data too large for proto buf (%u bytes)", len); + if (len > CONFIG_BUZZ_PROTO_SLAB_SIZE) { + LOG_ERR("Received data too large for proto buf (%u > %u)", len, CONFIG_BUZZ_PROTO_SLAB_SIZE); return; } diff --git a/protocol.md b/protocol.md index d166ee6..2beb39b 100644 --- a/protocol.md +++ b/protocol.md @@ -39,15 +39,16 @@ uint16_t payload_length // Little Endian |--------|------------|----------------|---------------------------------------| | `0x00` | `REQUEST` | Host → Device | Abfrage eines Datentyps | | `0x10` | `RESPONSE` | Device → Host | Antwort auf `REQUEST` | -| `0x11` | `ACK` | Host → Device | Flusskontrolle bei Stream-Transfers | +| `0x11` | `ACK` | Host ↔ Device | Flusskontrolle bei Stream-Transfers | | `0x12` | `ERROR` | Device → Host | Fehlerantwort mit Fehlercode | +| `0x13` | `SUCCESS` | Device → Host | Bestaetigung einer Operation | -### 4.2 Datei-Transfer (reserviert, noch nicht implementiert) -| Wert | Name | -|--------|--------------| -| `0x20` | `FILE_START` | -| `0x21` | `FILE_CHUNK` | -| `0x22` | `FILE_END` | +### 4.2 Datei-Transfer +| Wert | Name | Richtung | Beschreibung | +|--------|--------------|----------------|---------------------------------------------| +| `0x20` | `FILE_START` | Host ↔ Device | Beginn eines Dateitransfers | +| `0x21` | `FILE_CHUNK` | Host ↔ Device | Ein Datenblock des Dateitransfers | +| `0x22` | `FILE_END` | Host ↔ Device | Abschluss des Dateitransfers inkl. CRC32 | ### 4.3 Firmware-Transfer (reserviert, noch nicht implementiert) | Wert | Name | @@ -97,6 +98,13 @@ Definierte `data_type`-Werte: | `0x01` | `PROTO_INFO` | Protokollversion und Chunk-Groesse | | `0x02` | `DEVICE_INFO` | Geraeteinformationen (TBD) | | `0x03` | `FS_INFO` | Dateisystem-Statistik und Pfadnamen | +| `0x20` | `FILE_GET` | Datei vom Device anfordern | +| `0x21` | `FILE_PUT` | Datei auf das Device hochladen | +| `0x22` | `TAGS_GET` | Metadaten-Tags anfordern | +| `0x23` | `TAGS_PUT` | Metadaten-Tags schreiben | +| `0x24` | `RM_FILE` | Datei loeschen | +| `0x25` | `RENAME_FILE` | Datei umbenennen | +| `0x30` | `FW_UPDATE` | Firmware-Update starten | | `0x40` | `LS` | Verzeichnisliste starten | ### 6.1 `PROTO_INFO` (`0x01`) @@ -144,10 +152,36 @@ Wire-Format (Beispiel fuer Pfad `/a`): Das Device antwortet mit dem LS-Stream (siehe Abschnitt 8). -## 7. ACK- und ERROR-Frames +### 6.5 `RM_FILE` (`0x24`) — Datei loeschen +Request-Payload: +```c +uint8_t data_type; // 0x24 +uint8_t path_length; // Laenge des Pfads +char path[]; // Pfad ohne 0-Terminator +``` -### 7.1 ACK (`frame_type = 0x11`) — Host → Device -Wird waehrend eines laufenden LS-Streams gesendet, um dem Device Credits (Sendeerlaubnisse) zu erteilen. +### 6.6 `RENAME_FILE` (`0x25`) — Datei umbenennen +Request-Payload: +```c +uint8_t data_type; // 0x25 +uint8_t old_path_length; // Laenge des alten Pfads +uint8_t new_path_length; // Laenge des neuen Pfads +char paths[]; // Alter Pfad, direkt gefolgt vom neuen Pfad (beide ohne 0-Terminator) +``` + +### 6.7 `FILE_PUT` (`0x21`) / `TAGS_PUT` (`0x23`) — Upload initiieren +Request-Payload: +```c +uint8_t data_type; // 0x21 (Datei) oder 0x23 (Tags) +uint32_t total_size; // Dateigroesse in Bytes (LE) +char path[]; // Zielpfad ohne 0-Terminator +``` + +## 7. ACK-, ERROR- und SUCCESS-Frames + +### 7.1 ACK (`frame_type = 0x11`) — Host ↔ Device +Wird waehrend eines laufenden Stream-Transfers gesendet, um der sendenden Seite Credits (Sendeerlaubnisse) zu erteilen. +Bei einem Download (`LS` oder `FILE_GET`) sendet der Host das ACK. Bei einem Upload (`FILE_PUT` oder `TAGS_PUT`) sendet das Device das ACK. Format: ```c @@ -204,6 +238,24 @@ Fehlercode-Tabelle (Zephyr errno, positiver Wert): | 88 | `ENOSYS` | Funktion nicht implementiert | | 134 | `ENOTSUP` | Operation nicht unterstuetzt | +### 7.3 SUCCESS (`frame_type = 0x13`) — Device → Host +Bestaetigt den erfolgreichen Abschluss einer Operation, z. B. nach Beendigung eines Uploads oder einer Dateioperation (Loeschen, Umbenennen). + +Format: +```c +// Header: +uint8_t frame_type; // 0x13 +uint16_t payload_length; // 0x0001 + +// Payload: +uint8_t data_type; // Der Befehl, der erfolgreich war (z.B. 0x21 fuer FILE_PUT) +``` + +Wire-Format (Beispiel: Erfolg bei RM_FILE): +``` +[0x13][0x01 0x00][0x24] +``` + ## 8. LS-Stream (Verzeichnisliste) Der LS-Stream wird durch einen `REQUEST` mit `data_type = 0x40` ausgeloest und laeuft wie folgt ab: diff --git a/webpage/astro.config.mjs b/webpage/astro.config.mjs index f65a3d0..c2ff85f 100644 --- a/webpage/astro.config.mjs +++ b/webpage/astro.config.mjs @@ -1,12 +1,15 @@ // @ts-check import { defineConfig } from 'astro/config'; - import svelte from '@astrojs/svelte'; - import tailwindcss from '@tailwindcss/vite'; +const isProd = process.env.NODE_ENV === 'production'; + // https://astro.build/config export default defineConfig({ + base: isProd ? '/buzzer/' : '/', + site: 'https://home.iten.pro', + integrations: [svelte()], vite: { diff --git a/webpage/public/apple-touch-icon.png b/webpage/public/apple-touch-icon.png new file mode 100644 index 0000000..6ceb3fd Binary files /dev/null and b/webpage/public/apple-touch-icon.png differ diff --git a/webpage/public/favicon-96x96.png b/webpage/public/favicon-96x96.png new file mode 100644 index 0000000..ca6a5fe Binary files /dev/null and b/webpage/public/favicon-96x96.png differ diff --git a/webpage/public/favicon.ico b/webpage/public/favicon.ico index 7f48a94..cbd04a7 100644 Binary files a/webpage/public/favicon.ico and b/webpage/public/favicon.ico differ diff --git a/webpage/public/favicon.svg b/webpage/public/favicon.svg index f157bd1..e1094b5 100644 --- a/webpage/public/favicon.svg +++ b/webpage/public/favicon.svg @@ -1,9 +1,17 @@ - - - - + \ No newline at end of file diff --git a/webpage/public/site.webmanifest b/webpage/public/site.webmanifest new file mode 100644 index 0000000..33b8f87 --- /dev/null +++ b/webpage/public/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "Edis Buzzer", + "short_name": "Buzzer", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/webpage/public/web-app-manifest-192x192.png b/webpage/public/web-app-manifest-192x192.png new file mode 100644 index 0000000..9078cee Binary files /dev/null and b/webpage/public/web-app-manifest-192x192.png differ diff --git a/webpage/public/web-app-manifest-512x512.png b/webpage/public/web-app-manifest-512x512.png new file mode 100644 index 0000000..de11931 Binary files /dev/null and b/webpage/public/web-app-manifest-512x512.png differ diff --git a/webpage/src/components/AppGuard.svelte b/webpage/src/components/AppGuard.svelte index 9a678d6..0811395 100644 --- a/webpage/src/components/AppGuard.svelte +++ b/webpage/src/components/AppGuard.svelte @@ -6,7 +6,7 @@ onMount(async () => { performHardwareCheck(); - + if ($isBluetoothSupported) { const { restoreSession } = await import("../lib/bluetooth"); await restoreSession(); @@ -22,9 +22,11 @@ {:else if !$isBluetoothSupported}
+ class="fixed lg:h-screen inset-0 flex flex-col items-center justify-center p-0 lg:p-4 z-[100] bg-white lg:bg-transparent" + style="hyphens:auto;" + >

Dein Browser ist... suboptimal

🥺
@@ -34,10 +36,12 @@ Leider unterstützt dein Browser die benötigten Bluetooth-Funktionen nicht. Bitte versuche es mit einem aktuellen Chrome oder einem andern Chromium-basierten Browser. - Winzigweich Kante soll gerüchteweise auch Chromium-basiert sein... + Winzigweich Kante + soll gerüchteweise auch Chromium-basiert sein...

- Rundreise auf iOS unterstützt Bluetooth leider nicht, aber du kannst es mit einem vernünftigen Gerät oder Browser versuchen. + Rundreise auf iOS unterstützt Bluetooth leider nicht, aber du kannst es mit einem + vernünftigen Gerät oder Browser versuchen.

diff --git a/webpage/src/components/FileListItem.svelte b/webpage/src/components/FileListItem.svelte index 49e6d3b..bc4c7f2 100644 --- a/webpage/src/components/FileListItem.svelte +++ b/webpage/src/components/FileListItem.svelte @@ -15,17 +15,22 @@ WarningCircleIcon, } from "phosphor-svelte"; import { - isFetchingRemote, + isTransferingRemote, transferStats, transferDetails, buzzerAudioFiles, localAudioFiles, syncStateMap, + fsInfo, } from "../lib/store"; import { SETTINGS } from "../lib/settings"; import { tagEditorState } from "../lib/store"; import { tooltip } from "../lib/actions/tooltip"; + import { deleteRemoteFile } from "../lib/transport"; + import { deleteLocalFile } from "../lib/db"; + import { refreshRemote, refreshLocal } from "../lib/sync"; + import { addToast } from "../lib/toast"; export let file: BuzzerFile; export let type: "local" | "buzzer" = "buzzer"; @@ -37,7 +42,7 @@ $: myIndex = selectedFiles.findIndex((f) => f.name === file.name); $: state = (() => { - if (!file.selected || !$isFetchingRemote) return "default"; + if (!file.selected || !$isTransferingRemote) return "default"; if (file.name === $transferStats.currentFileName) return "active"; if (myIndex < currentIndex) return "done"; if (myIndex > currentIndex) return "pending"; @@ -93,7 +98,7 @@ })(); function toggleSelection() { - if ($isFetchingRemote) return; + if ($isTransferingRemote) return; if (type === "buzzer") { buzzerAudioFiles.update((files) => @@ -110,6 +115,35 @@ ), ); } + + async function handleDeleteClick() { + if (!confirm(`Möchten Sie die Datei "${file.name}" wirklich löschen?`)) { + menuOpen = false; + return; + } + + if (type === "buzzer") { + try { + const basePath = $fsInfo?.audioPath || "/lfs/a"; + const fullPath = `${basePath}/${file.name}`; + await deleteRemoteFile(fullPath); + addToast(`Datei ${file.name} erfolgreich vom Buzzer gelöscht.`, "success"); + await refreshRemote(); + } catch (error) { + console.error("Fehler beim Löschen:", error); + addToast("Fehler beim Löschen der Datei auf dem Buzzer.", "error"); + } + } else { + try { + await deleteLocalFile(file.name); + addToast(`Lokale Datei ${file.name} gelöscht.`, "success"); + await refreshLocal(); + } catch (error) { + console.error("Fehler beim Löschen:", error); + } + } + menuOpen = false; + } (menuOpen = false)} /> @@ -128,14 +162,14 @@ class="relative z-10 w-full text-left flex-1 px-3 py-1 pr-16 flex items-center border-l-4 transition-colors border-b border-b-border-card {file.selected ? 'border-l-blue-600' : 'border-l-transparent'} {file.selected && state !== 'active' ? 'bg-blue-50' : ''} - {!$isFetchingRemote && file.selected ? 'hover:bg-blue-100 cursor-pointer' : ''} - {!$isFetchingRemote && !file.selected + {!$isTransferingRemote && file.selected ? 'hover:bg-blue-100 cursor-pointer' : ''} + {!$isTransferingRemote && !file.selected ? 'hover:bg-slate-100 hover:border-l-blue-200 cursor-pointer' : ''} - {$isFetchingRemote ? 'cursor-default' : ''} + {$isTransferingRemote ? 'cursor-default' : ''} {state === 'pending' ? 'grayscale opacity-80' : ''}" on:click={toggleSelection} - disabled={$isFetchingRemote} + disabled={$isTransferingRemote} > @@ -198,10 +232,7 @@ diff --git a/webpage/src/components/FileMenuOverlay.svelte b/webpage/src/components/FileMenuOverlay.svelte index 42be340..68f43cd 100644 --- a/webpage/src/components/FileMenuOverlay.svelte +++ b/webpage/src/components/FileMenuOverlay.svelte @@ -1,7 +1,7 @@