Compare commits

6 Commits

29 changed files with 779 additions and 317 deletions

12
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "nrf-connect",
"request": "launch",
"name": "Launch firmware/build_nrf52840dk_debug",
"config": "${workspaceFolder}/firmware/build_nrf52840dk_debug",
"runToEntryPoint": "main"
}
]
}

View File

@@ -6,5 +6,8 @@
"nrf-connect.boardRoots": [ "nrf-connect.boardRoots": [
"${workspaceFolder}/firmware" "${workspaceFolder}/firmware"
], ],
"cmake.sourceDirectory": "C:/Projekte/buzzer_2/firmware/libs/ble_mgmt" "cmake.sourceDirectory": "C:/Projekte/buzzer_2/firmware/libs/ble_mgmt",
"nrf-connect.debugging.bindings": {
"${workspaceFolder}/firmware/build_nrf52840dk_debug": "Launch firmware/build_nrf52840dk_debug"
}
} }

View File

@@ -1,11 +1,2 @@
### Console / Logging: RTT
CONFIG_USE_SEGGER_RTT=y
CONFIG_CONSOLE=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n
CONFIG_LOG_BACKEND_UART=n
CONFIG_LOG_BACKEND_RTT=y
# Keep SPI NOR page layout aligned with generated LittleFS block size (4KB). # Keep SPI NOR page layout aligned with generated LittleFS block size (4KB).
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096

View File

@@ -1,5 +1,6 @@
board: board:
name: buzzy name: buzzy
full_name: Buzzy
vendor: iten vendor: iten
socs: socs:
- name: nrf52840 - name: nrf52840

View File

@@ -1,4 +0,0 @@
### Console / Logging: UART
CONFIG_UART_CONSOLE=y
CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_BACKEND_RTT=n

View File

@@ -33,4 +33,17 @@
pinctrl-0 = <&i2s0_default>; pinctrl-0 = <&i2s0_default>;
pinctrl-1 = <&i2s0_sleep>; pinctrl-1 = <&i2s0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
}; };
&mx25r64 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
ext_flash_lfs: partition@0 {
label = "ext-littlefs";
reg = <0x00000000 DT_SIZE_M(8)>;
};
};
};

View File

@@ -1,8 +1,23 @@
### Logging ### Logging
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_AUDIO_LOG_LEVEL_DBG=y CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_DEBUG=y
CONFIG_DEBUG_OPTIMIZATIONS=y CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_INIT_STACKS=y CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y CONFIG_THREAD_STACK_INFO=y
### Increase logging thread stack to prevent overflow when shell active
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
CONFIG_LOG_BUFFER_SIZE=4096
### Bluetooth subsystem logging (reduced noise)
CONFIG_BT_LOG_LEVEL_WRN=y
### Shell features shared by all debug variants
CONFIG_SHELL=y
CONFIG_SHELL_LOG_BACKEND=n
CONFIG_FILE_SYSTEM_SHELL=y
CONFIG_SHELL_STACK_SIZE=2048
CONFIG_FILE_SYSTEM_SHELL_LS_SIZE=y

15
firmware/debug_rtt.conf Normal file
View File

@@ -0,0 +1,15 @@
### Debug transport: RTT
CONFIG_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_LOG_BACKEND_RTT=y
### Shell over RTT
CONFIG_SHELL_BACKEND_RTT=y
CONFIG_SHELL_BACKEND_RTT_BUFFER=1
CONFIG_UART_CONSOLE=n
CONFIG_LOG_BACKEND_UART=n
### RTT Buffer
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=8192

10
firmware/debug_uart.conf Normal file
View File

@@ -0,0 +1,10 @@
### Debug transport: UART
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_LOG_BACKEND_UART=y
### Shell over UART
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_RTT_CONSOLE=n
CONFIG_LOG_BACKEND_RTT=n

View File

@@ -244,8 +244,10 @@ def main() -> None:
if staging_root.exists(): if staging_root.exists():
shutil.rmtree(staging_root) shutil.rmtree(staging_root)
out_sys = staging_root / "lfs" / "sys" # The LittleFS partition is mounted at /lfs in firmware, so image root
out_a = staging_root / "lfs" / "a" # must contain /sys and /a directly (not /lfs/sys and /lfs/a).
out_sys = staging_root / "sys"
out_a = staging_root / "a"
out_sys.mkdir(parents=True, exist_ok=True) out_sys.mkdir(parents=True, exist_ok=True)
out_a.mkdir(parents=True, exist_ok=True) out_a.mkdir(parents=True, exist_ok=True)

View File

@@ -1 +1 @@
nrfutil device --x-ext-mem-config-file "%~dp0buzzy.json" program --firmware "%~dp0lfs_external_flash.hex" --options verify=VERIFY_READ,reset=RESET_SYSTEM nrfutil device --x-ext-mem-config-file "%~dp0buzzy.json" program --firmware "%~dp0lfs_external_flash.hex" --options chip_erase_mode=ERASE_NONE,verify=VERIFY_READ,reset=RESET_SYSTEM,ext_mem_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh #!/usr/bin/env sh
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
nrfutil device --x-ext-mem-config-file "$SCRIPT_DIR/buzzy.json" program --firmware "$SCRIPT_DIR/lfs_external_flash.hex" --options verify=VERIFY_READ,reset=RESET_SYSTEM nrfutil device --x-ext-mem-config-file "$SCRIPT_DIR/buzzy.json" program --firmware "$SCRIPT_DIR/lfs_external_flash.hex" --options chip_erase_mode=ERASE_NONE,verify=VERIFY_READ,reset=RESET_SYSTEM,ext_mem_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE

View File

@@ -1,3 +1,4 @@
add_subdirectory(settings_mgmt)
add_subdirectory(fw_mgmt) add_subdirectory(fw_mgmt)
add_subdirectory(fs_mgmt) add_subdirectory(fs_mgmt)
add_subdirectory(ble_mgmt) add_subdirectory(ble_mgmt)

View File

@@ -1,6 +1,7 @@
rsource "settings_mgmt/Kconfig"
rsource "fw_mgmt/Kconfig" rsource "fw_mgmt/Kconfig"
rsource "fs_mgmt/Kconfig" rsource "fs_mgmt/Kconfig"
rsource "ble_mgmt/Kconfig" rsource "ble_mgmt/Kconfig"
rsource "buzz_proto/Kconfig" rsource "buzz_proto/Kconfig"
rsource "audio/Kconfig" rsource "audio/Kconfig"
rsource "event_mgmt/Kconfig" rsource "event_mgmt/Kconfig"

View File

@@ -3,87 +3,38 @@ menuconfig BLE_MGMT
default n default n
select BT select BT
select BT_PERIPHERAL select BT_PERIPHERAL
select BT_LOG_LEVEL_WARN
select BT_DEVICE_NAME_DYNAMIC select BT_DEVICE_NAME_DYNAMIC
help help
Library for initializing and managing Bluetooth functionality. Minimal BLE transport for the buzzer firmware.
if BLE_MGMT if BLE_MGMT
config BLE_MGMT_TX_QUEUE_DEPTH
int "BLE TX queue depth"
default 32
help
Number of notification payloads that can be queued in the BLE transport.
config BLE_MGMT_DEFAULT_DEVICE_NAME config BLE_MGMT_DEFAULT_DEVICE_NAME
string "Default Bluetooth Device Name" string "Default Bluetooth device name"
default "Edis Buzzer" default "Edis Buzzer"
config BLE_MGMT_ADV_INT_MIN help
int "Minimum Advertising Interval (in 0.625 ms units)" Device name used when ble_mgmt_init() is called with a NULL name.
default 160
help
Minimal advertising interval. 160 equals to 100ms.
config BLE_MGMT_ADV_INT_MAX
int "Maximum Advertising Interval (ms)"
default 160
help
Maximal advertising interval. 160 equals to 100ms.
# Airtime
config BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT
default 4000000
# MTU Setup
config BT_BUF_ACL_RX_SIZE
default 502
config BT_BUF_ACL_TX_SIZE
default 502
config BT_L2CAP_TX_MTU
default 498
config BT_CTLR_DATA_LENGTH_MAX
default 251
# Buffers
config BT_BUF_ACL_TX_COUNT
default 15
config BT_L2CAP_TX_BUF_COUNT
default 15
config BT_CONN_TX_MAX
default 15
config BT_CTLR_SDC_TX_PACKET_COUNT
default 15
config BT_CTLR_SDC_RX_PACKET_COUNT
default 15
config BT_BUF_EVT_RX_COUNT
default 16
# Callbacks
config BT_USER_PHY_UPDATE
default y
config BT_USER_DATA_LEN_UPDATE
default y
# Automatic updates
config BT_AUTO_PHY_UPDATE
default y
config BT_AUTO_DATA_LEN_UPDATE
default y
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
default y
# Preferred defaults
config BT_PERIPHERAL_PREF_MIN_INT
default 6
config BT_PERIPHERAL_PREF_MAX_INT
default 40
config BT_PERIPHERAL_PREF_LATENCY
default 0
config BT_PERIPHERAL_PREF_TIMEOUT
default 400
# Connections
config BT_MAX_CONN config BT_MAX_CONN
default 2 default 2
# BLE control/data handling runs in BT RX workqueue. Enforce a larger
# stack while BLE_MGMT is enabled to avoid overflows on connect/file ops.
config BT_RX_STACK_SIZE
range 3072 8192
default 3072
# Use larger BLE data path buffers for file transfer use-cases.
config BT_L2CAP_TX_MTU
default 498
config BT_BUF_ACL_RX_SIZE
default 502
config BT_BUF_ACL_TX_SIZE
default 502
config BT_CTLR_DATA_LENGTH_MAX
default 251
module = BLE_MGMT module = BLE_MGMT
module-str = ble_mgmt module-str = ble_mgmt

View File

@@ -17,7 +17,8 @@ int ble_mgmt_init(ble_mgmt_rx_cb_t rx_cb, const char *device_name);
* Sends data to the connected central device via a GATT characteristic. * Sends data to the connected central device via a GATT characteristic.
* @param data Pointer to the data buffer to send. * @param data Pointer to the data buffer to send.
* @param len Length of the data in bytes. * @param len Length of the data in bytes.
* @return 0 on success, -EACCES if notifications are not enabled, or a negative error code on failure. * @return 0 on success, -EAGAIN if no ATT link is ready yet, -EACCES if notifications are not enabled,
* or a negative error code on failure.
*/ */
int ble_mgmt_send(const uint8_t *data, uint16_t len); int ble_mgmt_send(const uint8_t *data, uint16_t len);

View File

@@ -1,14 +1,14 @@
#include <zephyr/bluetooth/bluetooth.h> #include <errno.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/conn.h>
#include <string.h> #include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/logging/log.h>
#include "ble_mgmt.h" #include "ble_mgmt.h"
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL); LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
@@ -20,18 +20,24 @@ LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
#define BUZZ_TX_UUID_VAL \ #define BUZZ_TX_UUID_VAL \
BT_UUID_128_ENCODE(0xe517d988, 0xbab5, 0x4574, 0x8479, 0x97c6cb115ca2) BT_UUID_128_ENCODE(0xe517d988, 0xbab5, 0x4574, 0x8479, 0x97c6cb115ca2)
#define DEFAULT_ATT_MTU 23U
#define GATT_NOTIFY_OVERHEAD 3U
#define MAX_ADV_NAME_LEN 29
struct ble_peer {
struct bt_conn *conn;
};
static struct bt_uuid_128 buzz_service_uuid = BT_UUID_INIT_128(BUZZ_SERVICE_UUID_VAL); 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_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 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 ble_mgmt_rx_cb_t app_rx_cb;
static bool notify_enabled = false; static bool ble_ready;
static bool advertising_active;
static uint16_t current_tx_mtu = 23; static uint16_t current_att_mtu = DEFAULT_ATT_MTU;
static uint16_t current_rx_mtu = 23;
#define MAX_ADV_NAME_LEN 29
static char current_device_name[MAX_ADV_NAME_LEN + 1]; static char current_device_name[MAX_ADV_NAME_LEN + 1];
static struct ble_peer peers[CONFIG_BT_MAX_CONN];
static const struct bt_data ad[] = { 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_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
@@ -44,219 +50,322 @@ static struct bt_data sd[] = {
static struct bt_le_adv_param adv_param = { static struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT, .id = BT_ID_DEFAULT,
.sid = 0,
.secondary_max_skip = 0,
.options = BT_LE_ADV_OPT_CONN, .options = BT_LE_ADV_OPT_CONN,
.interval_min = CONFIG_BLE_MGMT_ADV_INT_MIN, .interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
.interval_max = CONFIG_BLE_MGMT_ADV_INT_MAX, .interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
.peer = NULL,
}; };
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) 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); ARG_UNUSED(conn);
current_tx_mtu = tx;
current_rx_mtu = rx; current_att_mtu = MIN(tx, rx);
LOG_INF("ATT MTU updated to %u", current_att_mtu);
}
static size_t peer_count(void)
{
size_t count = 0;
for (size_t index = 0; index < ARRAY_SIZE(peers); ++index) {
if (peers[index].conn != NULL) {
++count;
}
}
return count;
}
static struct ble_peer *find_peer(struct bt_conn *conn)
{
for (size_t index = 0; index < ARRAY_SIZE(peers); ++index) {
if (peers[index].conn == conn) {
return &peers[index];
}
}
return NULL;
}
static struct ble_peer *reserve_peer(struct bt_conn *conn)
{
struct ble_peer *peer = find_peer(conn);
if (peer != NULL) {
return peer;
}
for (size_t index = 0; index < ARRAY_SIZE(peers); ++index) {
if (peers[index].conn == NULL) {
peers[index].conn = bt_conn_ref(conn);
return &peers[index];
}
}
return NULL;
}
static void release_peer(struct bt_conn *conn)
{
struct ble_peer *peer = find_peer(conn);
if (peer == NULL) {
return;
}
bt_conn_unref(peer->conn);
peer->conn = NULL;
}
static void set_device_name(const char *name)
{
const char *name_to_use = name != NULL ? name : CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME;
strncpy(current_device_name, name_to_use, MAX_ADV_NAME_LEN);
current_device_name[MAX_ADV_NAME_LEN] = '\0';
sd[0].data_len = strlen(current_device_name);
#ifdef CONFIG_BT_DEVICE_NAME_DYNAMIC
if (ble_ready) {
(void)bt_set_name(current_device_name);
}
#endif
}
static int start_advertising(void)
{
int rc;
if (!ble_ready || peer_count() >= CONFIG_BT_MAX_CONN || advertising_active) {
return 0;
}
rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (rc == -EALREADY) {
advertising_active = true;
return 0;
}
if (rc != 0) {
LOG_ERR("Failed to start advertising (err %d)", rc);
return rc;
}
advertising_active = true;
LOG_INF("Advertising as %s", current_device_name);
return 0;
}
static int restart_advertising(void)
{
int rc;
if (!ble_ready) {
return -EAGAIN;
}
if (advertising_active) {
rc = bt_le_adv_stop();
if ((rc != 0) && (rc != -EALREADY)) {
LOG_ERR("Failed to stop advertising (err %d)", rc);
return rc;
}
advertising_active = false;
}
return start_advertising();
} }
static ssize_t rx_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, 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) const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{ {
LOG_DBG("Received %u bytes", len); ARG_UNUSED(conn);
LOG_HEXDUMP_DBG(buf, len, "Data:"); ARG_UNUSED(attr);
ARG_UNUSED(flags);
if (app_rx_cb) if (offset != 0U) {
{ return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
if (app_rx_cb != NULL) {
app_rx_cb((const uint8_t *)buf, len); app_rx_cb((const uint8_t *)buf, len);
} }
return len; return len;
} }
static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{ {
notify_enabled = (value == BT_GATT_CCC_NOTIFY); ARG_UNUSED(attr);
LOG_INF("Notifications %s", notify_enabled ? "enabled" : "disabled"); LOG_INF("Notification state changed: 0x%04x", value);
} }
BT_GATT_SERVICE_DEFINE(ble_mgmt_svc, BT_GATT_SERVICE_DEFINE(ble_mgmt_svc,
BT_GATT_PRIMARY_SERVICE(&buzz_service_uuid), BT_GATT_PRIMARY_SERVICE(&buzz_service_uuid),
BT_GATT_CHARACTERISTIC(&buzz_rx_uuid.uuid, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_CHARACTERISTIC(&buzz_rx_uuid.uuid, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE, NULL, rx_cb, NULL), BT_GATT_PERM_WRITE, NULL, rx_cb, NULL),
BT_GATT_CHARACTERISTIC(&buzz_tx_uuid.uuid, BT_GATT_CHRC_NOTIFY, BT_GATT_CHARACTERISTIC(&buzz_tx_uuid.uuid, BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_NONE, NULL, NULL, NULL), BT_GATT_PERM_NONE, NULL, NULL, NULL),
BT_GATT_CCC(tx_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)); 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) static void connected(struct bt_conn *conn, uint8_t err)
{ {
if (err) char addr_str[BT_ADDR_LE_STR_LEN];
{ struct bt_conn_info info;
int rc;
if (err != 0U) {
LOG_ERR("Connection failed (err 0x%02x)", err); LOG_ERR("Connection failed (err 0x%02x)", err);
return; return;
} }
char addr_str[BT_ADDR_LE_STR_LEN]; advertising_active = false;
struct bt_conn_info info;
int rc = bt_conn_get_info(conn, &info); if (reserve_peer(conn) == NULL) {
if (rc == 0) LOG_ERR("No free BLE peer slot available");
{ return;
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
{ rc = bt_conn_get_info(conn, &info);
LOG_INF("Connected (info retrieval failed)"); if ((rc == 0) && (info.type == BT_CONN_TYPE_LE)) {
bt_addr_le_to_str(info.le.dst, addr_str, sizeof(addr_str));
LOG_INF("Connected to %s (%u/%u)", addr_str, (unsigned int)peer_count(),
CONFIG_BT_MAX_CONN);
} else {
LOG_INF("Connected (%u/%u)", (unsigned int)peer_count(), CONFIG_BT_MAX_CONN);
}
if (peer_count() < CONFIG_BT_MAX_CONN) {
(void)start_advertising();
} }
} }
static void disconnected(struct bt_conn *conn, uint8_t reason) static void disconnected(struct bt_conn *conn, uint8_t reason)
{ {
LOG_INF("Disconnected (reason 0x%02x)", reason); release_peer(conn);
advertising_active = false;
/* Startet Advertising mit dem global definierten Setup neu */ LOG_INF("Disconnected (reason 0x%02x, %u/%u active)", reason,
int rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); (unsigned int)peer_count(), CONFIG_BT_MAX_CONN);
if (rc)
{ if (peer_count() < CONFIG_BT_MAX_CONN) {
LOG_ERR("Advertising failed to restart (err %d)", rc); (void)start_advertising();
} }
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) = { BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected, .connected = connected,
.disconnected = disconnected, .disconnected = disconnected,
.le_param_updated = le_param_updated,
.le_phy_updated = le_phy_updated,
}; };
uint16_t ble_mgmt_get_max_payload(void)
{
uint16_t mtu = current_att_mtu;
#ifdef CONFIG_BT_L2CAP_TX_MTU
if (mtu > CONFIG_BT_L2CAP_TX_MTU) {
mtu = CONFIG_BT_L2CAP_TX_MTU;
}
#endif
return (mtu > GATT_NOTIFY_OVERHEAD) ? (mtu - GATT_NOTIFY_OVERHEAD) : 20U;
}
int ble_mgmt_send(const uint8_t *data, uint16_t len)
{
bool any_subscriber = false;
bool any_link_present = false;
int last_error = 0;
ARG_UNUSED(data);
ARG_UNUSED(len);
for (size_t index = 0; index < ARRAY_SIZE(peers); ++index) {
int rc;
if (peers[index].conn == NULL) {
continue;
}
any_link_present = true;
if (!bt_gatt_is_subscribed(peers[index].conn, &ble_mgmt_svc.attrs[4],
BT_GATT_CCC_NOTIFY)) {
continue;
}
any_subscriber = true;
do {
rc = bt_gatt_notify(peers[index].conn, &ble_mgmt_svc.attrs[4], data, len);
if (rc == -ENOMEM) {
k_sleep(K_MSEC(5));
}
} while (rc == -ENOMEM);
if (rc != 0) {
LOG_ERR("Failed to send notification (err %d)", rc);
last_error = rc;
}
}
if (!any_link_present || current_att_mtu <= DEFAULT_ATT_MTU) {
LOG_DBG("TX deferred: ATT not ready (links=%u, mtu=%u)",
(unsigned int)peer_count(), current_att_mtu);
return -EAGAIN;
}
if (!any_subscriber) {
LOG_DBG("TX blocked: no peer subscribed for notifications");
return -EACCES;
}
return last_error;
}
int ble_mgmt_update_adv_name(const char *new_name)
{
set_device_name(new_name);
if (peer_count() >= CONFIG_BT_MAX_CONN && !advertising_active) {
return 0;
}
return restart_advertising();
}
int ble_mgmt_init(ble_mgmt_rx_cb_t rx_cb, const char *device_name) int ble_mgmt_init(ble_mgmt_rx_cb_t rx_cb, const char *device_name)
{ {
int rc; int rc;
app_rx_cb = rx_cb;
static struct bt_gatt_cb gatt_callbacks = { static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated, .att_mtu_updated = att_mtu_updated,
}; };
bt_gatt_cb_register(&gatt_callbacks); LOG_INF("ble_mgmt_init: starting");
app_rx_cb = rx_cb;
current_att_mtu = DEFAULT_ATT_MTU;
LOG_INF("ble_mgmt_init: calling bt_enable");
rc = bt_enable(NULL); rc = bt_enable(NULL);
if (rc) if (rc != 0) {
{
LOG_ERR("Bluetooth init failed (err %d)", rc); LOG_ERR("Bluetooth init failed (err %d)", rc);
return rc; return rc;
} }
const char *name_to_use = (device_name != NULL) ? device_name : CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME; LOG_INF("ble_mgmt_init: bt_enable done, marking ready");
LOG_INF("BLE init: set_device_name"); ble_ready = true;
set_device_name(name_to_use); bt_gatt_cb_register(&gatt_callbacks);
advertising_active = false;
set_device_name(device_name);
LOG_INF("BLE init: bt_le_adv_start"); LOG_INF("ble_mgmt_init: calling start_advertising");
rc = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); rc = start_advertising();
if (rc) if (rc != 0) {
{ LOG_ERR("start_advertising failed (err %d)", rc);
LOG_ERR("Advertising failed to start (err %d)", rc);
return rc; return rc;
} }
LOG_INF("Bluetooth initialized. Adv-Name: %s", current_device_name); LOG_INF("ble_mgmt_init: complete");
return 0; return 0;
} }

View File

@@ -12,13 +12,16 @@
LOG_MODULE_REGISTER(fs_mgmt, CONFIG_FS_MGMT_LOG_LEVEL); LOG_MODULE_REGISTER(fs_mgmt, CONFIG_FS_MGMT_LOG_LEVEL);
/* Prefer external LittleFS partition when present, otherwise internal storage partition. */ /*
#if DT_NODE_EXISTS(DT_NODELABEL(ext_flash_lfs)) * Under sysbuild, Partition Manager generates PM_<name>_ID symbols.
* Without PM, we fall back to the DTS node label.
*/
#if defined(PM_littlefs_storage_ID)
#define FS_PARTITION_ID PM_littlefs_storage_ID
#elif DT_NODE_EXISTS(DT_NODELABEL(ext_flash_lfs))
#define FS_PARTITION_ID FIXED_PARTITION_ID(ext_flash_lfs) #define FS_PARTITION_ID FIXED_PARTITION_ID(ext_flash_lfs)
#elif DT_NODE_EXISTS(DT_NODELABEL(storage_partition))
#define FS_PARTITION_ID FIXED_PARTITION_ID(storage_partition)
#else #else
#error "No compatible LittleFS partition node label found (expected ext_flash_lfs or storage_partition)" #error "No compatible LittleFS partition found (expected PM littlefs_storage or DTS ext_flash_lfs)"
#endif #endif
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_storage_data); FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_storage_data);
@@ -35,8 +38,10 @@ static struct fs_mount_t fs_storage_mnt = {
.mnt_point = CONFIG_FS_MGMT_MOUNT_POINT, .mnt_point = CONFIG_FS_MGMT_MOUNT_POINT,
}; };
#if defined(CONFIG_PM_DEVICE) && !defined(CONFIG_FILE_SYSTEM_SHELL)
static int open_count = 0; static int open_count = 0;
static struct k_mutex flash_pm_lock; static struct k_mutex flash_pm_lock;
#endif
// #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 INITIAL_CREDITS CONFIG_BUZZ_PROTO_SLAB_COUNT
@@ -76,7 +81,9 @@ static struct
*/ */
static int fs_mgmt_pm_flash_suspend(void) static int fs_mgmt_pm_flash_suspend(void)
{ {
#if IS_ENABLED(CONFIG_PM_DEVICE) #if defined(CONFIG_FILE_SYSTEM_SHELL)
return 0;
#elif defined(CONFIG_PM_DEVICE)
if (!device_is_ready(flash_dev)) if (!device_is_ready(flash_dev))
{ {
return -ENODEV; return -ENODEV;
@@ -102,7 +109,7 @@ static int fs_mgmt_pm_flash_suspend(void)
} }
k_mutex_unlock(&flash_pm_lock); k_mutex_unlock(&flash_pm_lock);
#endif /* CONFIG_PM_DEVICE */ #endif /* CONFIG_FILE_SYSTEM_SHELL / CONFIG_PM_DEVICE */
return 0; return 0;
} }
@@ -113,7 +120,9 @@ static int fs_mgmt_pm_flash_suspend(void)
*/ */
static int fs_mgmt_pm_flash_resume(void) static int fs_mgmt_pm_flash_resume(void)
{ {
#if IS_ENABLED(CONFIG_PM_DEVICE) #if defined(CONFIG_FILE_SYSTEM_SHELL)
return 0;
#elif defined(CONFIG_PM_DEVICE)
if (!device_is_ready(flash_dev)) if (!device_is_ready(flash_dev))
return -ENODEV; return -ENODEV;
@@ -131,7 +140,7 @@ static int fs_mgmt_pm_flash_resume(void)
open_count++; open_count++;
k_mutex_unlock(&flash_pm_lock); k_mutex_unlock(&flash_pm_lock);
#endif /* CONFIG_PM_DEVICE */ #endif /* CONFIG_FILE_SYSTEM_SHELL / CONFIG_PM_DEVICE */
return 0; return 0;
} }
@@ -179,6 +188,20 @@ int fs_mgmt_pm_unlink(const char *path)
{ {
LOG_DBG("PM Unlinking file '%s'", path); LOG_DBG("PM Unlinking file '%s'", path);
fs_mgmt_pm_flash_resume(); fs_mgmt_pm_flash_resume();
struct fs_dirent entry;
int stat_rc = fs_stat(path, &entry);
if (stat_rc == -ENOENT)
{
fs_mgmt_pm_flash_suspend();
return 0;
}
if (stat_rc < 0)
{
fs_mgmt_pm_flash_suspend();
return stat_rc;
}
int rc = fs_unlink(path); int rc = fs_unlink(path);
fs_mgmt_pm_flash_suspend(); fs_mgmt_pm_flash_suspend();
return rc; return rc;
@@ -397,7 +420,9 @@ int fs_mgmt_pm_mkdir_recursive(char *path)
static int fs_mgmt_init(void) static int fs_mgmt_init(void)
{ {
#if defined(CONFIG_PM_DEVICE) && !defined(CONFIG_FILE_SYSTEM_SHELL)
k_mutex_init(&flash_pm_lock); k_mutex_init(&flash_pm_lock);
#endif
if (!device_is_ready(flash_dev)) if (!device_is_ready(flash_dev))
{ {

View File

@@ -0,0 +1,5 @@
if(CONFIG_SETTINGS_MGMT)
zephyr_library()
zephyr_library_sources(src/settings_mgmt.c)
zephyr_include_directories(include)
endif()

View File

@@ -0,0 +1,14 @@
menuconfig SETTINGS_MGMT
bool "Settings Management"
default y
select SETTINGS
select ZMS
help
Library for initializing and managing the settings subsystem.
ZMS (Zephyr Memory Storage) backend is automatically selected when ZMS is enabled.
if SETTINGS_MGMT
module = SETTINGS_MGMT
module-str = settings_mgmt
source "subsys/logging/Kconfig.template.log_config"
endif # SETTINGS_MGMT

View File

@@ -0,0 +1,34 @@
#ifndef SETTINGS_MGMT_H
#define SETTINGS_MGMT_H
#include <zephyr/types.h>
struct app_settings_t {
/* System */
char dev_name[33];
/* Audio */
uint8_t vol;
uint8_t shuffle_mode; /* 0: Rnd, 1: No-Rep */
/* BLE */
uint32_t ble_timeout; /* Seconds, 0xFFFFFFFF = forever */
uint16_t ble_interval; /* Milliseconds */
/* Power & Calibration */
uint8_t chg_mode; /* 0: 500mA, 1: 1A, 2: Auto */
uint16_t off_threshold;/* mV */
int16_t adc_gain;
int16_t adc_offset;
};
/* Global access to the current runtime configuration */
extern struct app_settings_t app_cfg;
/* Initializes the settings subsystem and loads values from flash */
int settings_mgmt_init(void);
/* Sets a setting via string path (for your protocol) */
int settings_mgmt_set_by_path(const char *path, const void *value, size_t len);
#endif

View File

@@ -0,0 +1,239 @@
#include <zephyr/settings/settings.h>
#include <zephyr/logging/log.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "settings_mgmt.h"
LOG_MODULE_REGISTER(settings_mgmt, LOG_LEVEL_INF);
/* Sanity check limits */
#define VOL_MIN 0
#define VOL_MAX 100
#define SHUFFLE_MODE_MIN 0
#define SHUFFLE_MODE_MAX 1
#define BLE_TIMEOUT_MIN 1000
#define BLE_TIMEOUT_MAX 0xFFFFFFFF
#define BLE_INTERVAL_MIN 10
#define BLE_INTERVAL_MAX 10000
#define CHG_MODE_MIN 0
#define CHG_MODE_MAX 2
#define OFF_THRESHOLD_MIN 2900 /* mV, LiPo cut-off */
#define OFF_THRESHOLD_MAX 3500 /* mV, LiPo nominal */
#define ADC_GAIN_MIN -100
#define ADC_GAIN_MAX 100
#define ADC_OFFSET_MIN -1000
#define ADC_OFFSET_MAX 1000
/* The "Source of Truth" in RAM */
struct app_settings_t app_cfg = {
.dev_name = "Edis Buzzer",
.vol = 100, /* 0-100 % */
.shuffle_mode = 0,
.ble_timeout = 0xFFFFFFFF,
.ble_interval = 100,
.chg_mode = 2, /* Auto */
.off_threshold = 3000, /* mV */
.adc_gain = 0,
.adc_offset = 0,
};
static int read_exact(settings_read_cb read_cb, void *cb_arg,
void *dst, size_t expected_len)
{
int rc = read_cb(cb_arg, dst, expected_len);
if (rc < 0) {
return rc;
}
if ((size_t)rc != expected_len) {
return -EINVAL;
}
return 0;
}
/* Sanity check helpers */
static int check_uint8_range(uint8_t val, uint8_t min, uint8_t max,
const char *name)
{
if (val < min || val > max) {
LOG_WRN("%s out of range: %u (valid: %u-%u)", name, val, min, max);
return -EINVAL;
}
return 0;
}
static int check_uint16_range(uint16_t val, uint16_t min, uint16_t max,
const char *name)
{
if (val < min || val > max) {
LOG_WRN("%s out of range: %u (valid: %u-%u)", name, val, min, max);
return -EINVAL;
}
return 0;
}
static int check_uint32_range(uint32_t val, uint32_t min, uint32_t max,
const char *name)
{
if (val < min || val > max) {
LOG_WRN("%s out of range: %u (valid: %u-%u)", name, val, min, max);
return -EINVAL;
}
return 0;
}
static int check_int16_range(int16_t val, int16_t min, int16_t max,
const char *name)
{
if (val < min || val > max) {
LOG_WRN("%s out of range: %d (valid: %d-%d)", name, val, min, max);
return -EINVAL;
}
return 0;
}
/* Callback: Called by settings_load() or settings_runtime_set() */
static int buzzy_settings_set(const char *name, size_t len,
settings_read_cb read_cb, void *cb_arg)
{
const char *next;
int rc;
/* Path matching: "buzzy/sys/name" -> name is "sys/name" here */
if (settings_name_steq(name, "sys/name", &next) && !next) {
rc = read_cb(cb_arg, app_cfg.dev_name, sizeof(app_cfg.dev_name) - 1);
if (rc >= 0) app_cfg.dev_name[rc] = '\0';
return 0;
}
if (settings_name_steq(name, "audio/vol", &next) && !next) {
uint8_t vol;
rc = read_exact(read_cb, cb_arg, &vol, sizeof(vol));
if (rc == 0) {
rc = check_uint8_range(vol, VOL_MIN, VOL_MAX, "audio/vol");
if (rc == 0) {
app_cfg.vol = vol;
}
}
return rc;
}
if ((settings_name_steq(name, "audio/shuffle", &next) ||
settings_name_steq(name, "audio/shuffle_mode", &next)) && !next) {
uint8_t shuffle;
rc = read_exact(read_cb, cb_arg, &shuffle, sizeof(shuffle));
if (rc == 0) {
rc = check_uint8_range(shuffle, SHUFFLE_MODE_MIN, SHUFFLE_MODE_MAX, "shuffle_mode");
if (rc == 0) {
app_cfg.shuffle_mode = shuffle;
}
}
return rc;
}
if (settings_name_steq(name, "ble/to", &next) && !next) {
uint32_t timeout;
rc = read_exact(read_cb, cb_arg, &timeout, sizeof(timeout));
if (rc == 0) {
rc = check_uint32_range(timeout, BLE_TIMEOUT_MIN, BLE_TIMEOUT_MAX, "ble/to");
if (rc == 0) {
app_cfg.ble_timeout = timeout;
}
}
return rc;
}
if ((settings_name_steq(name, "ble/interval", &next) ||
settings_name_steq(name, "ble/int", &next)) && !next) {
uint16_t interval;
rc = read_exact(read_cb, cb_arg, &interval, sizeof(interval));
if (rc == 0) {
rc = check_uint16_range(interval, BLE_INTERVAL_MIN, BLE_INTERVAL_MAX, "ble/interval");
if (rc == 0) {
app_cfg.ble_interval = interval;
}
}
return rc;
}
if ((settings_name_steq(name, "power/chg_mode", &next) ||
settings_name_steq(name, "sys/chg_mode", &next)) && !next) {
uint8_t chg_mode;
rc = read_exact(read_cb, cb_arg, &chg_mode, sizeof(chg_mode));
if (rc == 0) {
rc = check_uint8_range(chg_mode, CHG_MODE_MIN, CHG_MODE_MAX, "chg_mode");
if (rc == 0) {
app_cfg.chg_mode = chg_mode;
}
}
return rc;
}
if ((settings_name_steq(name, "power/off_threshold", &next) ||
settings_name_steq(name, "sys/off_threshold", &next)) && !next) {
uint16_t threshold;
rc = read_exact(read_cb, cb_arg, &threshold, sizeof(threshold));
if (rc == 0) {
rc = check_uint16_range(threshold, OFF_THRESHOLD_MIN, OFF_THRESHOLD_MAX, "off_threshold [mV]");
if (rc == 0) {
app_cfg.off_threshold = threshold;
}
}
return rc;
}
if (settings_name_steq(name, "adc/gain", &next) && !next) {
int16_t gain;
rc = read_exact(read_cb, cb_arg, &gain, sizeof(gain));
if (rc == 0) {
rc = check_int16_range(gain, ADC_GAIN_MIN, ADC_GAIN_MAX, "adc/gain");
if (rc == 0) {
app_cfg.adc_gain = gain;
}
}
return rc;
}
if (settings_name_steq(name, "adc/offset", &next) && !next) {
int16_t offset;
rc = read_exact(read_cb, cb_arg, &offset, sizeof(offset));
if (rc == 0) {
rc = check_int16_range(offset, ADC_OFFSET_MIN, ADC_OFFSET_MAX, "adc/offset");
if (rc == 0) {
app_cfg.adc_offset = offset;
}
}
return rc;
}
return -ENOENT;
}
struct settings_handler buzzy_handler = {
.name = "buzzy",
.h_set = buzzy_settings_set
};
int settings_mgmt_set_by_path(const char *path, const void *value, size_t len)
{
char full_path[64];
snprintf(full_path, sizeof(full_path), "buzzy/%s", path);
/* Schreibt in ZMS UND triggert buzzy_settings_set zur RAM-Aktualisierung */
return settings_runtime_set(full_path, value, len);
}
int settings_mgmt_init(void)
{
int rc = settings_subsys_init();
if (rc) return rc;
rc = settings_register(&buzzy_handler);
if (rc) return rc;
/* Lädt alle gespeicherten Werte aus dem ZMS in die app_cfg Struktur */
return settings_load();
}

View File

@@ -1,4 +1,39 @@
# External Flash # Static Partition Manager layout for sysbuild builds.
# Keep this aligned with boards/iten/buzzy/buzzy.dts internal flash partitions.
mcuboot:
address: 0x0
size: 0xC000
region: flash_primary
mcuboot_pad:
address: 0xC000
size: 0x200
region: flash_primary
app:
address: 0xC200
size: 0x75E00
region: flash_primary
mcuboot_primary:
address: 0xC000
size: 0x76000
region: flash_primary
span: [mcuboot_pad, app]
mcuboot_secondary:
address: 0x82000
size: 0x76000
region: flash_primary
# Internal flash tail partition (8 x 4 KiB pages) for ZMS/settings-style usage.
storage:
address: 0xF8000
size: 0x8000
region: flash_primary
# External flash LittleFS image area.
littlefs_storage: littlefs_storage:
address: 0x0 address: 0x0
size: 0x800000 size: 0x800000

View File

@@ -1,39 +0,0 @@
# mcuboot:
# address: 0x0
# size: 0xC000
# region: flash_primary
# # Primary Slot: Start bleibt 0xC000, Größe 200KB (0x32000)
# mcuboot_primary:
# address: 0xC000
# size: 0x32000
# region: flash_primary
# mcuboot_pad:
# address: 0xC000
# size: 0x200
# region: flash_primary
# # Die App startet nach dem Padding des Primary Slots
# app:
# address: 0xC200
# size: 0x31E00 # (0x32000 - 0x200)
# region: flash_primary
# # Secondary Slot: Startet jetzt bei 0xC000 + 0x32000 = 0x3E000
# mcuboot_secondary:
# address: 0x3E000
# size: 0x32000
# region: flash_primary
# # NVS storage am Ende des Flashs, 16KB (0x4000)
# settings_storage:
# address: 0xFC000
# size: 0x4000
# region: flash_primary
# External Flash
littlefs_storage:
address: 0x0
size: 0x800000 # 8MB
region: external_flash

View File

@@ -1,6 +1,9 @@
### Bluetooth ### Bluetooth
CONFIG_BLE_MGMT=y CONFIG_BLE_MGMT=y
### Audio
CONFIG_BUZZ_AUDIO=n
### Error handling ### Error handling
CONFIG_HW_STACK_PROTECTION=y CONFIG_HW_STACK_PROTECTION=y
CONFIG_RESET_ON_FATAL_ERROR=y CONFIG_RESET_ON_FATAL_ERROR=y
@@ -10,3 +13,4 @@ CONFIG_PM_DEVICE=y
### Stack ### Stack
CONFIG_MAIN_STACK_SIZE=2048 CONFIG_MAIN_STACK_SIZE=2048
CONFIG_BT_RX_STACK_SIZE=4096

View File

@@ -2,9 +2,8 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <string.h> #include <string.h>
#include "fs_mgmt.h"
#include "buzz_proto.h" #include "buzz_proto.h"
#include "fw_mgmt.h" // #include "fw_mgmt.h"
// #include "audio.h" // #include "audio.h"
LOG_MODULE_REGISTER(main); LOG_MODULE_REGISTER(main);

View File

@@ -287,7 +287,14 @@ export function parseIncomingFrame(view: DataView, sender: FrameSender) {
case FRAME.ERROR: case FRAME.ERROR:
const errorCode = view.getUint16(3, true); const errorCode = view.getUint16(3, true);
console.error(`Received error frame with code: 0x${errorCode.toString(16)}`); const errorInfo = ZEPHYR_ERRORS[errorCode];
if (errorInfo) {
console.error(
`Received error frame: 0x${errorCode.toString(16).padStart(2, '0')} (${errorInfo.zephyr}) - ${errorInfo.text}`
);
} else {
console.error(`Received error frame with code: 0x${errorCode.toString(16).padStart(2, '0')}`);
}
showErrorToast(errorCode); showErrorToast(errorCode);
if (lsReject) { if (lsReject) {
const currentReject = lsReject; const currentReject = lsReject;
@@ -361,6 +368,7 @@ export function buildFWInfoRequest(): ArrayBuffer {
export function buildLSRequest(path: string): ArrayBuffer { export function buildLSRequest(path: string): ArrayBuffer {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const pathBytes = encoder.encode(path); const pathBytes = encoder.encode(path);
console.debug(`[Protocol] LS request for path: ${path}`);
const buffer = new ArrayBuffer(4 + pathBytes.length); const buffer = new ArrayBuffer(4 + pathBytes.length);
const view = new DataView(buffer); const view = new DataView(buffer);
@@ -442,6 +450,7 @@ export function setFileGetResolver(
export function buildFileGetRequest(path: string): ArrayBuffer { export function buildFileGetRequest(path: string): ArrayBuffer {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const pathBytes = encoder.encode(path); const pathBytes = encoder.encode(path);
console.debug(`[Protocol] FILE_GET request for path: ${path}`);
const buffer = new ArrayBuffer(4 + pathBytes.length); const buffer = new ArrayBuffer(4 + pathBytes.length);
const view = new DataView(buffer); const view = new DataView(buffer);
@@ -458,6 +467,7 @@ export function buildFileGetRequest(path: string): ArrayBuffer {
export function buildTagsGetRequest(path: string): ArrayBuffer { export function buildTagsGetRequest(path: string): ArrayBuffer {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const pathBytes = encoder.encode(path); const pathBytes = encoder.encode(path);
console.debug(`[Protocol] TAGS_GET request for path: ${path}`);
const buffer = new ArrayBuffer(4 + pathBytes.length); const buffer = new ArrayBuffer(4 + pathBytes.length);
const view = new DataView(buffer); const view = new DataView(buffer);

View File

@@ -34,12 +34,18 @@ export async function refreshRemote() {
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));
const currentFsInfo = get(fsInfo); const currentFsInfo = get(fsInfo);
console.debug("[Sync] Remote FS info:", currentFsInfo);
// Sequenzielle Abfrage via Transport-Layer // Sequenzielle Abfrage via Transport-Layer
const sysFiles = await fetchDirectory(currentFsInfo?.sysPath || "/lfs/sys"); const sysPath = currentFsInfo?.sysPath || "/lfs/sys";
const audioPath = currentFsInfo?.audioPath || "/lfs/a";
console.debug(`[Sync] Listing system directory: ${sysPath}`);
const sysFiles = await fetchDirectory(sysPath);
buzzerSysFiles.set(sysFiles.map(mapToBuzzerFile)); buzzerSysFiles.set(sysFiles.map(mapToBuzzerFile));
const audioFiles = await fetchDirectory(currentFsInfo?.audioPath || "/lfs/a"); console.debug(`[Sync] Listing audio directory: ${audioPath}`);
const audioFiles = await fetchDirectory(audioPath);
let mappedAudio = audioFiles.map(mapToBuzzerFile); let mappedAudio = audioFiles.map(mapToBuzzerFile);
// Dateien sofort im UI anzeigen, bevor die Tags geladen sind // Dateien sofort im UI anzeigen, bevor die Tags geladen sind
@@ -101,6 +107,7 @@ export async function refreshLocal() {
export async function downloadSelectedFiles() { export async function downloadSelectedFiles() {
const files = get(buzzerAudioFiles).filter(f => f.selected); const files = get(buzzerAudioFiles).filter(f => f.selected);
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a"; const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
console.debug(`[Sync] Download prefix: ${pathPrefix}`);
if (files.length === 0) { if (files.length === 0) {
addToast("Keine Dateien zum Herunterladen ausgewählt.", "warning"); addToast("Keine Dateien zum Herunterladen ausgewählt.", "warning");
@@ -181,6 +188,7 @@ export async function deleteSelectedLocalFiles() {
export async function deleteSelectedRemoteFiles() { export async function deleteSelectedRemoteFiles() {
const files = get(buzzerAudioFiles).filter(f => f.selected); const files = get(buzzerAudioFiles).filter(f => f.selected);
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a"; const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
console.debug(`[Sync] Delete prefix: ${pathPrefix}`);
if (files.length === 0) return; if (files.length === 0) return;
@@ -208,6 +216,7 @@ export async function deleteSelectedRemoteFiles() {
export async function uploadSelectedFiles() { export async function uploadSelectedFiles() {
const files = get(localAudioFiles).filter(f => f.selected); const files = get(localAudioFiles).filter(f => f.selected);
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a"; const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
console.debug(`[Sync] Upload prefix: ${pathPrefix}`);
if (files.length === 0) { if (files.length === 0) {
addToast("Keine Dateien zum Hochladen ausgewählt.", "warning"); addToast("Keine Dateien zum Hochladen ausgewählt.", "warning");

View File

@@ -66,6 +66,8 @@ export async function fetchDirectory(path: string): Promise<any[]> {
} }
isListing = true; isListing = true;
console.debug(`[Transport] fetchDirectory(${path})`);
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
// Dem Parser sagen, wen er bei Erfolg/Fehler anrufen soll // Dem Parser sagen, wen er bei Erfolg/Fehler anrufen soll
setLsResolver( setLsResolver(
@@ -95,6 +97,8 @@ export async function getFile(path: string): Promise<boolean> {
} }
isFileTransferring = true; isFileTransferring = true;
console.debug(`[Transport] getFile(${path})`);
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
setFileGetResolver( setFileGetResolver(
(result: any) => { isFileTransferring = false; resolve(result.success); }, (result: any) => { isFileTransferring = false; resolve(result.success); },
@@ -116,6 +120,8 @@ export async function getTags(path: string): Promise<Blob> {
} }
isFileTransferring = true; isFileTransferring = true;
console.debug(`[Transport] getTags(${path})`);
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
setFileGetResolver( setFileGetResolver(
(result: any) => { (result: any) => {