Compare commits
7 Commits
upgrade/nc
...
87cba0b419
| Author | SHA1 | Date | |
|---|---|---|---|
| 87cba0b419 | |||
| 52bab32309 | |||
| dd51f45084 | |||
| 980e23e51a | |||
| 5557c78179 | |||
| 32fe244fcf | |||
| 5ae098962d |
12
.vscode/launch.json
vendored
Normal file
12
.vscode/launch.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -6,5 +6,8 @@
|
||||
"nrf-connect.boardRoots": [
|
||||
"${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"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
VERSION_MAJOR = 0
|
||||
VERSION_MINOR = 0
|
||||
PATCHLEVEL = 6
|
||||
PATCHLEVEL = 77
|
||||
VERSION_TWEAK = 0
|
||||
#if (IS_ENABLED(CONFIG_LOG))
|
||||
EXTRAVERSION = debug
|
||||
|
||||
@@ -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).
|
||||
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
||||
# Keep QSPI NOR page layout aligned with generated LittleFS block size (4KB).
|
||||
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
||||
|
||||
@@ -18,47 +18,47 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi3_default: spi3_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||
};
|
||||
};
|
||||
// spi3_default: spi3_default {
|
||||
// group1 {
|
||||
// psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||
// <NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||
// <NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||
// };
|
||||
// };
|
||||
|
||||
spi3_sleep: spi3_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
// spi3_sleep: spi3_sleep {
|
||||
// group1 {
|
||||
// psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||
// <NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||
// <NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||
// low-power-enable;
|
||||
// };
|
||||
// };
|
||||
|
||||
/*
|
||||
* Optional future QSPI pinctrl states (keep disabled for now).
|
||||
* Use these when switching from &spi3 to &qspi in buzzy.dts.
|
||||
*/
|
||||
// qspi_default: qspi_default {
|
||||
// group1 {
|
||||
// psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||
// <NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||
// <NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||
// <NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||
// <NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||
// <NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||
// };
|
||||
// };
|
||||
qspi_default: qspi_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||
<NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||
<NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||
<NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||
<NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||
<NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||
};
|
||||
};
|
||||
|
||||
// qspi_sleep: qspi_sleep {
|
||||
// group1 {
|
||||
// psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||
// <NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||
// <NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||
// <NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||
// <NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||
// <NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||
// low-power-enable;
|
||||
// };
|
||||
// };
|
||||
qspi_sleep: qspi_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||
<NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||
<NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||
<NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||
<NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||
<NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -54,7 +54,7 @@
|
||||
charger_status {
|
||||
compatible = "gpio-keys";
|
||||
chg_status: chg_status {
|
||||
gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||
label = "ETA6003_CHG_STATUS";
|
||||
};
|
||||
};
|
||||
@@ -137,17 +137,52 @@
|
||||
* SO/SIO1-> P0.30
|
||||
* CS -> P0.05
|
||||
*/
|
||||
&spi3 {
|
||||
// &spi3 {
|
||||
// status = "okay";
|
||||
// pinctrl-0 = <&spi3_default>;
|
||||
// pinctrl-1 = <&spi3_sleep>;
|
||||
// pinctrl-names = "default", "sleep";
|
||||
// cs-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
||||
|
||||
// mx25r64: flash@0 {
|
||||
// compatible = "jedec,spi-nor";
|
||||
// reg = <0>;
|
||||
// spi-max-frequency = <8000000>;
|
||||
// jedec-id = [c2 28 17];
|
||||
// size = <DT_SIZE_M(64)>;
|
||||
// has-dpd;
|
||||
// t-enter-dpd = <10000>;
|
||||
// t-exit-dpd = <35000>;
|
||||
|
||||
// partitions {
|
||||
// compatible = "fixed-partitions";
|
||||
// #address-cells = <1>;
|
||||
// #size-cells = <1>;
|
||||
|
||||
// ext_flash_lfs: partition@0 {
|
||||
// label = "ext-littlefs";
|
||||
// reg = <0x00000000 DT_SIZE_M(8)>;
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
|
||||
/*
|
||||
* Optional future QSPI variant (keep disabled for now):
|
||||
* - Disable &spi3 block above.
|
||||
* - Enable &qspi block below.
|
||||
* - Keep the same flash partition layout.
|
||||
*/
|
||||
&qspi {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi3_default>;
|
||||
pinctrl-1 = <&spi3_sleep>;
|
||||
pinctrl-0 = <&qspi_default>;
|
||||
pinctrl-1 = <&qspi_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
cs-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
||||
|
||||
mx25r64: flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
compatible = "nordic,qspi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
sck-frequency = <32000000>;
|
||||
jedec-id = [c2 28 17];
|
||||
size = <DT_SIZE_M(64)>;
|
||||
has-dpd;
|
||||
@@ -167,47 +202,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Optional future QSPI variant (keep disabled for now):
|
||||
* - Disable &spi3 block above.
|
||||
* - Enable &qspi block below.
|
||||
* - Keep the same flash partition layout.
|
||||
*/
|
||||
// &qspi {
|
||||
// status = "okay";
|
||||
// pinctrl-0 = <&qspi_default>;
|
||||
// pinctrl-1 = <&qspi_sleep>;
|
||||
// pinctrl-names = "default", "sleep";
|
||||
|
||||
// mx25r64: flash@0 {
|
||||
// compatible = "nordic,qspi-nor";
|
||||
// reg = <0>;
|
||||
// jedec-id = [c2 28 17];
|
||||
// size = <DT_SIZE_M(64)>;
|
||||
// has-dpd;
|
||||
// t-enter-dpd = <10000>;
|
||||
// t-exit-dpd = <35000>;
|
||||
|
||||
// /* Net mapping from hardware: *
|
||||
// * SCK=P0.02, CSN=P0.05, IO0=P0.29, IO1=P0.30, IO2=P0.31, IO3=P1.13
|
||||
// */
|
||||
// sck-pin = <2>;
|
||||
// csn-pins = <5>;
|
||||
// io-pins = <29>, <30>, <31>, <45>;
|
||||
|
||||
// partitions {
|
||||
// compatible = "fixed-partitions";
|
||||
// #address-cells = <1>;
|
||||
// #size-cells = <1>;
|
||||
|
||||
// ext_flash_lfs: partition@0 {
|
||||
// label = "ext-littlefs";
|
||||
// reg = <0x00000000 DT_SIZE_M(8)>;
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Console / Logging: UART
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_LOG_BACKEND_UART=y
|
||||
CONFIG_LOG_BACKEND_RTT=n
|
||||
@@ -33,4 +33,17 @@
|
||||
pinctrl-0 = <&i2s0_default>;
|
||||
pinctrl-1 = <&i2s0_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)>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
22
firmware/build_output.txt
Normal file
22
firmware/build_output.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
-- west build: making build dir /Users/edi/nrf_playground/buzzer_2/firmware/build pristine
|
||||
WARNING: This looks like a fresh build and BOARD is unknown; so it probably won't work. To fix, use --board=<your-board>.
|
||||
Note: to silence the above message, run 'west config build.board_warn false'
|
||||
-- west build: generating a build system
|
||||
Loading Zephyr module(s) (Zephyr base): sysbuild_default
|
||||
-- Found Python3: /opt/nordic/ncs/toolchains/322ac893fe/opt/python@3.12/bin/python3.12 (found suitable version "3.12.4", minimum required is "3.10") found components: Interpreter
|
||||
-- Cache files will be written to: /Users/edi/Library/Caches/zephyr
|
||||
-- Found west (found suitable version "1.4.0", minimum required is "0.14.0")
|
||||
CMake Error at /opt/nordic/ncs/v3.2.1/zephyr/cmake/modules/extensions.cmake:3518 (message):
|
||||
BOARD is not being defined on the CMake command-line, in the environment or
|
||||
by the app.
|
||||
Call Stack (most recent call first):
|
||||
/opt/nordic/ncs/v3.2.1/zephyr/cmake/modules/boards.cmake:61 (zephyr_check_cache)
|
||||
cmake/modules/sysbuild_default.cmake:15 (include)
|
||||
/opt/nordic/ncs/v3.2.1/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:75 (include)
|
||||
/opt/nordic/ncs/v3.2.1/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
|
||||
/opt/nordic/ncs/v3.2.1/zephyr/share/sysbuild-package/cmake/SysbuildConfig.cmake:8 (include)
|
||||
template/CMakeLists.txt:10 (find_package)
|
||||
|
||||
|
||||
-- Configuring incomplete, errors occurred!
|
||||
FATAL ERROR: command exited with status 1: /opt/homebrew/bin/cmake -DWEST_PYTHON=/opt/nordic/ncs/toolchains/322ac893fe/opt/python@3.12/bin/python3.12 -B/Users/edi/nrf_playground/buzzer_2/firmware/build -GNinja -S/opt/nordic/ncs/v3.2.1/zephyr/share/sysbuild -DAPP_DIR:PATH=/Users/edi/nrf_playground/buzzer_2/firmware
|
||||
@@ -1,8 +1,28 @@
|
||||
### Logging
|
||||
CONFIG_LOG=y
|
||||
CONFIG_AUDIO_LOG_LEVEL_DBG=y
|
||||
CONFIG_LOG_MODE_IMMEDIATE=y
|
||||
CONFIG_DEBUG=y
|
||||
|
||||
CONFIG_DEBUG_OPTIMIZATIONS=y
|
||||
|
||||
CONFIG_INIT_STACKS=y
|
||||
CONFIG_THREAD_STACK_INFO=y
|
||||
|
||||
### Lib logging levels
|
||||
CONFIG_BATT_MGMT_LOG_LEVEL_DBG=y
|
||||
# CONFIG_BATT_MGMT_BLINK_INTERVAL_LOGGING=y
|
||||
CONFIG_USB_MGMT_LOG_LEVEL_DBG=y
|
||||
|
||||
### Bluetooth
|
||||
CONFIG_BLE_MGMT=n
|
||||
CONFIG_BT_LOG_LEVEL_WRN=n
|
||||
|
||||
### Audio
|
||||
CONFIG_BUZZ_AUDIO=n
|
||||
|
||||
### 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
15
firmware/debug_rtt.conf
Normal 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
10
firmware/debug_uart.conf
Normal 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
|
||||
@@ -244,8 +244,10 @@ def main() -> None:
|
||||
if staging_root.exists():
|
||||
shutil.rmtree(staging_root)
|
||||
|
||||
out_sys = staging_root / "lfs" / "sys"
|
||||
out_a = staging_root / "lfs" / "a"
|
||||
# The LittleFS partition is mounted at /lfs in firmware, so image root
|
||||
# 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_a.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
add_subdirectory(settings_mgmt)
|
||||
add_subdirectory(fw_mgmt)
|
||||
add_subdirectory(fs_mgmt)
|
||||
add_subdirectory(ble_mgmt)
|
||||
add_subdirectory(buzz_proto)
|
||||
add_subdirectory(audio)
|
||||
add_subdirectory(event_mgmt)
|
||||
add_subdirectory(event_mgmt)
|
||||
add_subdirectory(batt_mgmt)
|
||||
add_subdirectory(usb_mgmt)
|
||||
@@ -1,6 +1,9 @@
|
||||
rsource "settings_mgmt/Kconfig"
|
||||
rsource "fw_mgmt/Kconfig"
|
||||
rsource "fs_mgmt/Kconfig"
|
||||
rsource "ble_mgmt/Kconfig"
|
||||
rsource "buzz_proto/Kconfig"
|
||||
rsource "audio/Kconfig"
|
||||
rsource "event_mgmt/Kconfig"
|
||||
rsource "event_mgmt/Kconfig"
|
||||
rsource "batt_mgmt/Kconfig"
|
||||
rsource "usb_mgmt/Kconfig"
|
||||
|
||||
@@ -89,6 +89,15 @@ static void audio_set_state(enum audio_thread_state_t new_state, const char *rea
|
||||
{
|
||||
enum audio_thread_state_t old_state = atomic_get(&thread_state);
|
||||
|
||||
if ((old_state == AUDIO_IDLE) && (new_state != AUDIO_IDLE))
|
||||
{
|
||||
event_mgmt_set_event(EVENT_MGMT_AUDIO_ACTIVE);
|
||||
}
|
||||
else if ((old_state != AUDIO_IDLE) && (new_state == AUDIO_IDLE))
|
||||
{
|
||||
event_mgmt_set_event(EVENT_MGMT_AUDIO_IDLE);
|
||||
}
|
||||
|
||||
if (old_state != new_state)
|
||||
{
|
||||
LOG_INF("Audio state %s -> %s (%s)",
|
||||
|
||||
5
firmware/libs/batt_mgmt/CMakeLists.txt
Normal file
5
firmware/libs/batt_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_BATT_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_library_sources(src/batt_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
96
firmware/libs/batt_mgmt/Kconfig
Normal file
96
firmware/libs/batt_mgmt/Kconfig
Normal file
@@ -0,0 +1,96 @@
|
||||
menuconfig BATT_MGMT
|
||||
bool "Battery Management"
|
||||
default y
|
||||
select ADC
|
||||
select EVENT_MGMT
|
||||
select GPIO
|
||||
select USB_MGMT
|
||||
select NRFX_SAADC
|
||||
help
|
||||
Library for initializing and managing the battery subsystem.
|
||||
|
||||
if BATT_MGMT
|
||||
config BATT_MGMT_EMPTY_THRESHOLD
|
||||
int "Battery Empty Voltage (mV)"
|
||||
default 3400
|
||||
help
|
||||
Voltage threshold treated as empty battery level (0/5).
|
||||
|
||||
config BATT_MGMT_SHUTDOWN_THRESHOLD
|
||||
int "Critical shutdown voltage (mV)"
|
||||
default 3300
|
||||
help
|
||||
If measured battery voltage is less than or equal to this threshold,
|
||||
the device will power off to protect the battery.
|
||||
|
||||
config BATT_MGMT_FULL_THRESHOLD
|
||||
int "Battery Full Voltage (mV)"
|
||||
default 3980
|
||||
help
|
||||
Set the voltage level (in millivolts) that represents a full battery. Default is 3980 mV.
|
||||
|
||||
config BATT_MGMT_80P_THRESHOLD
|
||||
int "Battery 80% Voltage (mV)"
|
||||
default 3820
|
||||
help
|
||||
Set the voltage level (in millivolts) that represents 80% battery. Default is 3820 mV.
|
||||
|
||||
config BATT_MGMT_50P_THRESHOLD
|
||||
int "Battery 50% Voltage (mV)"
|
||||
default 3720
|
||||
help
|
||||
Set the voltage level (in millivolts) that represents 50% battery. Default is 3720 mV.
|
||||
|
||||
config BATT_MGMT_20P_THRESHOLD
|
||||
int "Battery 20% Voltage (mV)"
|
||||
default 3620
|
||||
help
|
||||
Set the voltage level (in millivolts) that represents 20% battery. Default is 3620 mV.
|
||||
|
||||
config BATT_MGMT_CHG_BLINKING_WINDOW_MS
|
||||
int "Charger blinking detection window (ms)"
|
||||
default 700
|
||||
help
|
||||
Time window to detect charger pin blinking (state changes). If the pin state
|
||||
changes within this window, it's considered blinking (error state).
|
||||
|
||||
config BATT_MGMT_STANDBY_MEASURE_INTERVAL_MIN
|
||||
int "Standby measurement interval (minutes)"
|
||||
default 360
|
||||
help
|
||||
Measurement interval while idle in standby (no charging, no BLE, no audio).
|
||||
|
||||
config BATT_MGMT_ACTIVE_MEASURE_INTERVAL_MIN
|
||||
int "Active measurement interval (minutes)"
|
||||
default 1
|
||||
help
|
||||
Measurement interval while charging or BLE connected.
|
||||
|
||||
config BATT_MGMT_AUDIO_COOLDOWN_SEC
|
||||
int "Audio cooldown before next measure (seconds)"
|
||||
default 60
|
||||
help
|
||||
Delay before restarting periodic battery measurement after audio playback ends.
|
||||
|
||||
config BATT_MGMT_MONITOR_THREAD_STACK_SIZE
|
||||
int "Battery monitor thread stack size"
|
||||
default 1024
|
||||
help
|
||||
Stack size for the USB-triggered charger status monitor thread.
|
||||
|
||||
config BATT_MGMT_MONITOR_THREAD_PRIORITY
|
||||
int "Battery monitor thread priority"
|
||||
default 7
|
||||
help
|
||||
Cooperative priority for the monitor thread (lower number = higher priority).
|
||||
|
||||
config BATT_MGMT_BLINK_INTERVAL_LOGGING
|
||||
bool "Enable logging of charger pin blinking intervals"
|
||||
default n
|
||||
help
|
||||
If enabled, logs the time intervals between charger pin state changes, which can help diagnose charger connection issues.
|
||||
|
||||
module = BATT_MGMT
|
||||
module-str = batt_mgmt
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
endif # BATT_MGMT
|
||||
107
firmware/libs/batt_mgmt/include/batt_mgmt.h
Normal file
107
firmware/libs/batt_mgmt/include/batt_mgmt.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef BATT_MGMT_H
|
||||
#define BATT_MGMT_H
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define BATT_MGMT_OVERSAMPLING_8X 8U
|
||||
#define BATT_MGMT_OVERSAMPLING_16X 16U
|
||||
|
||||
typedef enum {
|
||||
BATT_STATE_DISCHARGING = 0,
|
||||
BATT_STATE_FULL,
|
||||
BATT_STATE_CHARGING,
|
||||
BATT_STATE_ERROR,
|
||||
BATT_STATE_UNKNOWN,
|
||||
} batt_mgmt_state_t;
|
||||
|
||||
typedef struct {
|
||||
batt_mgmt_state_t state;
|
||||
uint8_t level;
|
||||
uint8_t percent;
|
||||
int32_t voltage_mv;
|
||||
} batt_mgmt_info_t;
|
||||
|
||||
/**
|
||||
* @brief Measure battery VDDH voltage.
|
||||
*
|
||||
* @param oversampling Oversampling factor (BATT_MGMT_OVERSAMPLING_8X or BATT_MGMT_OVERSAMPLING_16X).
|
||||
* @param vddh_mv Pointer to store the result in millivolts.
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int batt_mgmt_measure_vddh_mv(uint8_t oversampling, int32_t *vddh_mv);
|
||||
|
||||
/**
|
||||
* @brief Get display voltage in millivolts.
|
||||
*
|
||||
* Returns the voltage value intended for UI/protocol display. Currently this is
|
||||
* the latest measured VDDH value cached by batt_mgmt.
|
||||
*
|
||||
* @param vddh_mv Pointer receiving display voltage in mV.
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int batt_mgmt_get_display_voltage_mv(int32_t *vddh_mv);
|
||||
|
||||
/**
|
||||
* @brief Get battery level bucket (0..4) from configured thresholds.
|
||||
*
|
||||
* Level mapping:
|
||||
* - 0: below 20% threshold
|
||||
* - 1: 20%..49%
|
||||
* - 2: 50%..79%
|
||||
* - 3: 80%..99%
|
||||
* - 4: full and above
|
||||
*
|
||||
* @param level Pointer receiving level in range 0..4.
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int batt_mgmt_get_battery_level(uint8_t *level);
|
||||
|
||||
/**
|
||||
* @brief Get battery percentage (0-100) from configured voltage thresholds.
|
||||
*
|
||||
* Uses piecewise linear interpolation over:
|
||||
* - EMPTY..20%
|
||||
* - 20%..50%
|
||||
* - 50%..80%
|
||||
* - 80%..FULL
|
||||
*
|
||||
* @param percent Pointer receiving percentage in range 0..100.
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int batt_mgmt_get_battery_percent(uint8_t *percent);
|
||||
|
||||
/**
|
||||
* @brief Get raw charger status (GPIO level).
|
||||
*
|
||||
* Returns the current logic level of the charger status pin (typically from ETA6003).
|
||||
* This is the **raw GPIO status**, not a processed state machine result.
|
||||
*
|
||||
* @return true if charger pin is high (e.g., battery is "charging"), false if low.
|
||||
*/
|
||||
bool batt_mgmt_get_charger_status(void);
|
||||
|
||||
/**
|
||||
* @brief Get processed battery state with blinking detection.
|
||||
*
|
||||
* Returns a derived state based on:
|
||||
* - USB VBUS presence (from usb_mgmt)
|
||||
* - Charger status GPIO level (from batt_mgmt_get_charger_status)
|
||||
* - Blinking detection (state changes within CONFIG_BATT_MGMT_CHG_BLINKING_WINDOW_MS)
|
||||
*
|
||||
* Note: This differs from batt_mgmt_get_charger_status() which returns the **raw** GPIO level.
|
||||
*
|
||||
* @return Battery state enum (DISCHARGING, FULL, CHARGING, or ERROR if blinking detected).
|
||||
*/
|
||||
batt_mgmt_state_t batt_mgmt_get_battery_state(void);
|
||||
|
||||
/**
|
||||
* @brief Get all battery information in one call.
|
||||
*
|
||||
* @param info Pointer receiving state, level (0..4), percent (0..100), and voltage in mV.
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int batt_mgmt_get_info(batt_mgmt_info_t *info);
|
||||
|
||||
#endif
|
||||
|
||||
591
firmware/libs/batt_mgmt/src/batt_mgmt.c
Normal file
591
firmware/libs/batt_mgmt/src/batt_mgmt.c
Normal file
@@ -0,0 +1,591 @@
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <nrfx_saadc.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "batt_mgmt.h"
|
||||
#include "usb_mgmt.h"
|
||||
|
||||
LOG_MODULE_REGISTER(batt_mgmt, CONFIG_BATT_MGMT_LOG_LEVEL);
|
||||
|
||||
#define BATT_VDDH_DIVIDER_FACTOR 5
|
||||
|
||||
#define BATT_VDDH_ADC_NODE DT_PATH(zephyr_user)
|
||||
static const struct adc_dt_spec batt_vddh_adc = ADC_DT_SPEC_GET_BY_NAME(BATT_VDDH_ADC_NODE, vddh);
|
||||
|
||||
#define BATT_CHG_STATUS_NODE DT_ALIAS(chg_status)
|
||||
static const struct gpio_dt_spec batt_chg_status = GPIO_DT_SPEC_GET(BATT_CHG_STATUS_NODE, gpios);
|
||||
|
||||
static bool batt_mgmt_ready;
|
||||
static int64_t batt_mgmt_last_chg_change_ms;
|
||||
static int64_t batt_mgmt_last_offset_calib_ms;
|
||||
static int32_t batt_mgmt_last_vddh_mv;
|
||||
static bool batt_mgmt_chg_interrupt_enabled;
|
||||
static K_THREAD_STACK_DEFINE(batt_monitor_stack, CONFIG_BATT_MGMT_MONITOR_THREAD_STACK_SIZE);
|
||||
static struct k_thread batt_monitor_thread_data;
|
||||
static struct k_sem batt_mgmt_saadc_calib_sem;
|
||||
static int batt_mgmt_saadc_calib_rc;
|
||||
|
||||
static void batt_mgmt_saadc_calib_handler(nrfx_saadc_evt_t const *p_event)
|
||||
{
|
||||
if ((p_event == NULL) || (p_event->type != NRFX_SAADC_EVT_CALIBRATEDONE))
|
||||
{
|
||||
batt_mgmt_saadc_calib_rc = -EIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
batt_mgmt_saadc_calib_rc = 0;
|
||||
}
|
||||
|
||||
k_sem_give(&batt_mgmt_saadc_calib_sem);
|
||||
}
|
||||
|
||||
static int batt_mgmt_calibrate_offset_if_needed(bool force)
|
||||
{
|
||||
int rc;
|
||||
int64_t now_ms = k_uptime_get();
|
||||
int64_t calib_interval_ms = (int64_t)CONFIG_BATT_MGMT_STANDBY_MEASURE_INTERVAL_MIN * 60 * 1000;
|
||||
|
||||
if (!force && (now_ms - batt_mgmt_last_offset_calib_ms < calib_interval_ms))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (k_sem_take(&batt_mgmt_saadc_calib_sem, K_NO_WAIT) == 0)
|
||||
{
|
||||
}
|
||||
|
||||
batt_mgmt_saadc_calib_rc = -EIO;
|
||||
rc = nrfx_saadc_offset_calibrate(batt_mgmt_saadc_calib_handler);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = k_sem_take(&batt_mgmt_saadc_calib_sem, K_MSEC(100));
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (batt_mgmt_saadc_calib_rc < 0)
|
||||
{
|
||||
return batt_mgmt_saadc_calib_rc;
|
||||
}
|
||||
|
||||
batt_mgmt_last_offset_calib_ms = k_uptime_get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batt_mgmt_to_adc_oversampling(uint8_t oversampling, uint8_t *adc_oversampling)
|
||||
{
|
||||
if (adc_oversampling == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (oversampling)
|
||||
{
|
||||
case BATT_MGMT_OVERSAMPLING_8X:
|
||||
*adc_oversampling = 3U;
|
||||
return 0;
|
||||
case BATT_MGMT_OVERSAMPLING_16X:
|
||||
*adc_oversampling = 4U;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int batt_mgmt_measure_vddh_mv(uint8_t oversampling, int32_t *vddh_mv)
|
||||
{
|
||||
int rc;
|
||||
uint8_t adc_oversampling;
|
||||
int16_t raw_sample;
|
||||
int32_t input_mv;
|
||||
struct adc_sequence sequence = {
|
||||
.buffer = &raw_sample,
|
||||
.buffer_size = sizeof(raw_sample),
|
||||
};
|
||||
|
||||
if (vddh_mv == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!batt_mgmt_ready)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_calibrate_offset_if_needed(false);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_WRN("ADC offset calibration failed before measurement: %d", rc);
|
||||
}
|
||||
|
||||
rc = batt_mgmt_to_adc_oversampling(oversampling, &adc_oversampling);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = adc_sequence_init_dt(&batt_vddh_adc, &sequence);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
sequence.oversampling = adc_oversampling;
|
||||
|
||||
rc = adc_read_dt(&batt_vddh_adc, &sequence);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
input_mv = raw_sample;
|
||||
rc = adc_raw_to_millivolts_dt(&batt_vddh_adc, &input_mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
*vddh_mv = input_mv * BATT_VDDH_DIVIDER_FACTOR;
|
||||
batt_mgmt_last_vddh_mv = *vddh_mv;
|
||||
|
||||
LOG_DBG("Battery VDDH: %d mV (raw=%d, os=%ux)", *vddh_mv, raw_sample, oversampling);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batt_mgmt_linear_percent(int32_t mv, int32_t lo_mv, int32_t hi_mv,
|
||||
uint8_t lo_pct, uint8_t hi_pct, uint8_t *out_pct)
|
||||
{
|
||||
int32_t num;
|
||||
int32_t den;
|
||||
int32_t pct;
|
||||
|
||||
if (out_pct == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hi_mv <= lo_mv)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mv <= lo_mv)
|
||||
{
|
||||
*out_pct = lo_pct;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mv >= hi_mv)
|
||||
{
|
||||
*out_pct = hi_pct;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = (mv - lo_mv) * (int32_t)(hi_pct - lo_pct);
|
||||
den = hi_mv - lo_mv;
|
||||
pct = (int32_t)lo_pct + (num / den);
|
||||
|
||||
if (pct < 0)
|
||||
{
|
||||
pct = 0;
|
||||
}
|
||||
else if (pct > 100)
|
||||
{
|
||||
pct = 100;
|
||||
}
|
||||
|
||||
*out_pct = (uint8_t)pct;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batt_mgmt_get_display_voltage_mv(int32_t *vddh_mv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (vddh_mv == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!batt_mgmt_ready)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (batt_mgmt_last_vddh_mv <= 0)
|
||||
{
|
||||
rc = batt_mgmt_measure_vddh_mv(BATT_MGMT_OVERSAMPLING_16X, &batt_mgmt_last_vddh_mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
*vddh_mv = batt_mgmt_last_vddh_mv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batt_mgmt_get_battery_level(uint8_t *level)
|
||||
{
|
||||
int32_t mv;
|
||||
int rc;
|
||||
|
||||
if (level == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_get_display_voltage_mv(&mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (mv >= CONFIG_BATT_MGMT_FULL_THRESHOLD)
|
||||
{
|
||||
*level = 4U;
|
||||
}
|
||||
else if (mv >= CONFIG_BATT_MGMT_80P_THRESHOLD)
|
||||
{
|
||||
*level = 3U;
|
||||
}
|
||||
else if (mv >= CONFIG_BATT_MGMT_50P_THRESHOLD)
|
||||
{
|
||||
*level = 2U;
|
||||
}
|
||||
else if (mv >= CONFIG_BATT_MGMT_20P_THRESHOLD)
|
||||
{
|
||||
*level = 1U;
|
||||
}
|
||||
else
|
||||
{
|
||||
*level = 0U;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batt_mgmt_get_battery_percent(uint8_t *percent)
|
||||
{
|
||||
int32_t mv;
|
||||
int rc;
|
||||
|
||||
if (percent == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_get_display_voltage_mv(&mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (mv <= CONFIG_BATT_MGMT_EMPTY_THRESHOLD)
|
||||
{
|
||||
*percent = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mv >= CONFIG_BATT_MGMT_FULL_THRESHOLD)
|
||||
{
|
||||
*percent = 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mv < CONFIG_BATT_MGMT_20P_THRESHOLD)
|
||||
{
|
||||
return batt_mgmt_linear_percent(mv,
|
||||
CONFIG_BATT_MGMT_EMPTY_THRESHOLD,
|
||||
CONFIG_BATT_MGMT_20P_THRESHOLD,
|
||||
0, 20, percent);
|
||||
}
|
||||
|
||||
if (mv < CONFIG_BATT_MGMT_50P_THRESHOLD)
|
||||
{
|
||||
return batt_mgmt_linear_percent(mv,
|
||||
CONFIG_BATT_MGMT_20P_THRESHOLD,
|
||||
CONFIG_BATT_MGMT_50P_THRESHOLD,
|
||||
20, 50, percent);
|
||||
}
|
||||
|
||||
if (mv < CONFIG_BATT_MGMT_80P_THRESHOLD)
|
||||
{
|
||||
return batt_mgmt_linear_percent(mv,
|
||||
CONFIG_BATT_MGMT_50P_THRESHOLD,
|
||||
CONFIG_BATT_MGMT_80P_THRESHOLD,
|
||||
50, 80, percent);
|
||||
}
|
||||
|
||||
return batt_mgmt_linear_percent(mv,
|
||||
CONFIG_BATT_MGMT_80P_THRESHOLD,
|
||||
CONFIG_BATT_MGMT_FULL_THRESHOLD,
|
||||
80, 100, percent);
|
||||
}
|
||||
|
||||
static void batt_mgmt_chg_interrupt_handler(const struct device *dev,
|
||||
struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
int64_t now_ms = k_uptime_get();
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(cb);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
#ifdef CONFIG_BATT_MGMT_BLINK_INTERVAL_LOGGING
|
||||
int64_t delta_ms = now_ms - batt_mgmt_last_chg_change_ms;
|
||||
LOG_DBG("CHG interrupt: delta=%lld ms", delta_ms);
|
||||
#endif
|
||||
|
||||
batt_mgmt_last_chg_change_ms = now_ms;
|
||||
}
|
||||
|
||||
static int batt_mgmt_enable_chg_interrupt(void)
|
||||
{
|
||||
int rc;
|
||||
static struct gpio_callback batt_chg_cb;
|
||||
|
||||
if (batt_mgmt_chg_interrupt_enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&batt_chg_status))
|
||||
{
|
||||
LOG_ERR("CHG GPIO device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure_dt(&batt_chg_status, GPIO_INPUT);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_ERR("Failed to configure CHG GPIO: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
gpio_init_callback(&batt_chg_cb, batt_mgmt_chg_interrupt_handler, BIT(batt_chg_status.pin));
|
||||
rc = gpio_add_callback(batt_chg_status.port, &batt_chg_cb);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_ERR("Failed to add CHG callback: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = gpio_pin_interrupt_configure_dt(&batt_chg_status, GPIO_INT_EDGE_BOTH);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_ERR("Failed to configure CHG interrupt: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
batt_mgmt_chg_interrupt_enabled = true;
|
||||
batt_mgmt_last_chg_change_ms = k_uptime_get();
|
||||
LOG_INF("CHG interrupt enabled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batt_mgmt_disable_chg_interrupt(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!batt_mgmt_chg_interrupt_enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = gpio_pin_interrupt_configure_dt(&batt_chg_status, GPIO_INT_DISABLE);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_WRN("Failed to disable CHG interrupt: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure(batt_chg_status.port, batt_chg_status.pin, GPIO_DISCONNECTED);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_WRN("Failed to disconnect CHG GPIO: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
batt_mgmt_chg_interrupt_enabled = false;
|
||||
LOG_DBG("CHG interrupt disabled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool batt_mgmt_get_charger_status(void)
|
||||
{
|
||||
if (!batt_mgmt_chg_interrupt_enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return gpio_pin_get_dt(&batt_chg_status);
|
||||
}
|
||||
|
||||
batt_mgmt_state_t batt_mgmt_get_battery_state(void)
|
||||
{
|
||||
int64_t now_ms = k_uptime_get();
|
||||
int64_t delta_ms = now_ms - batt_mgmt_last_chg_change_ms;
|
||||
bool chg_high = batt_mgmt_get_charger_status();
|
||||
bool usb_present = usb_mgmt_is_vbus_present();
|
||||
|
||||
/* Blinking detection: state change within window */
|
||||
if (delta_ms < CONFIG_BATT_MGMT_CHG_BLINKING_WINDOW_MS)
|
||||
{
|
||||
LOG_DBG("Blinking detected (delta=%lld ms)", delta_ms);
|
||||
return BATT_STATE_ERROR;
|
||||
}
|
||||
|
||||
/* Stable states */
|
||||
if (chg_high)
|
||||
{
|
||||
return BATT_STATE_CHARGING;
|
||||
}
|
||||
|
||||
if (usb_present)
|
||||
{
|
||||
return BATT_STATE_FULL;
|
||||
}
|
||||
|
||||
return BATT_STATE_DISCHARGING;
|
||||
}
|
||||
|
||||
int batt_mgmt_get_info(batt_mgmt_info_t *info)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_get_display_voltage_mv(&info->voltage_mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_get_battery_level(&info->level);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = batt_mgmt_get_battery_percent(&info->percent);
|
||||
if (rc < 0)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->state = batt_mgmt_get_battery_state();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void batt_mgmt_monitor_thread(void *unused1, void *unused2, void *unused3)
|
||||
{
|
||||
(void)unused1;
|
||||
(void)unused2;
|
||||
(void)unused3;
|
||||
|
||||
if (!usb_mgmt_wait_ready(K_SECONDS(2)))
|
||||
{
|
||||
LOG_WRN("usb_mgmt not ready after timeout; using current VBUS state");
|
||||
}
|
||||
|
||||
/* Sync initial state in case VBUS was already present before this thread started. */
|
||||
bool vbus_present = usb_mgmt_is_vbus_present();
|
||||
|
||||
if (vbus_present)
|
||||
{
|
||||
(void)batt_mgmt_enable_chg_interrupt();
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)batt_mgmt_disable_chg_interrupt();
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t events = k_event_wait(&usb_mgmt_events,
|
||||
USB_MGMT_VBUS_CONNECTED | USB_MGMT_VBUS_DISCONNECTED,
|
||||
false, K_FOREVER);
|
||||
|
||||
if (events & USB_MGMT_VBUS_CONNECTED)
|
||||
{
|
||||
(void)batt_mgmt_enable_chg_interrupt();
|
||||
}
|
||||
|
||||
if (events & USB_MGMT_VBUS_DISCONNECTED)
|
||||
{
|
||||
(void)batt_mgmt_disable_chg_interrupt();
|
||||
}
|
||||
|
||||
k_event_clear(&usb_mgmt_events, USB_MGMT_VBUS_CONNECTED | USB_MGMT_VBUS_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
static int batt_mgmt_init(void)
|
||||
{
|
||||
int32_t boot_vddh_mv;
|
||||
int rc;
|
||||
|
||||
if (!adc_is_ready_dt(&batt_vddh_adc))
|
||||
{
|
||||
LOG_ERR("VDDH ADC device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = adc_channel_setup_dt(&batt_vddh_adc);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_ERR("VDDH ADC channel setup failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
k_sem_init(&batt_mgmt_saadc_calib_sem, 0, 1);
|
||||
batt_mgmt_last_offset_calib_ms = 0;
|
||||
|
||||
batt_mgmt_ready = true;
|
||||
|
||||
rc = batt_mgmt_calibrate_offset_if_needed(true);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_WRN("Initial ADC offset calibration failed: %d", rc);
|
||||
}
|
||||
|
||||
rc = batt_mgmt_measure_vddh_mv(BATT_MGMT_OVERSAMPLING_16X, &boot_vddh_mv);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_WRN("Initial battery measurement failed: %d", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
batt_mgmt_last_vddh_mv = boot_vddh_mv;
|
||||
LOG_DBG("Initial battery VDDH: %d mV", boot_vddh_mv);
|
||||
|
||||
k_thread_create(&batt_monitor_thread_data,
|
||||
batt_monitor_stack,
|
||||
K_THREAD_STACK_SIZEOF(batt_monitor_stack),
|
||||
batt_mgmt_monitor_thread,
|
||||
NULL, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_BATT_MGMT_MONITOR_THREAD_PRIORITY),
|
||||
0, K_NO_WAIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(batt_mgmt_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
@@ -3,87 +3,38 @@ menuconfig BLE_MGMT
|
||||
default n
|
||||
select BT
|
||||
select BT_PERIPHERAL
|
||||
select BT_LOG_LEVEL_WARN
|
||||
select BT_DEVICE_NAME_DYNAMIC
|
||||
help
|
||||
Library for initializing and managing Bluetooth functionality.
|
||||
Minimal BLE transport for the buzzer firmware.
|
||||
|
||||
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
|
||||
string "Default Bluetooth Device Name"
|
||||
default "Edis Buzzer"
|
||||
config BLE_MGMT_ADV_INT_MIN
|
||||
int "Minimum Advertising Interval (in 0.625 ms units)"
|
||||
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
|
||||
string "Default Bluetooth device name"
|
||||
default "Edis Buzzer 2.0"
|
||||
help
|
||||
Device name used when ble_mgmt_init() is called with a NULL name.
|
||||
|
||||
# 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
|
||||
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-str = ble_mgmt
|
||||
|
||||
@@ -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.
|
||||
* @param data Pointer to the data buffer to send.
|
||||
* @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);
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include <zephyr/bluetooth/bluetooth.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 <errno.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 "event_mgmt.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||
|
||||
@@ -20,18 +21,24 @@ LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||
#define BUZZ_TX_UUID_VAL \
|
||||
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_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 ble_mgmt_rx_cb_t app_rx_cb;
|
||||
static bool ble_ready;
|
||||
static bool advertising_active;
|
||||
static uint16_t current_att_mtu = DEFAULT_ATT_MTU;
|
||||
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[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
@@ -44,219 +51,326 @@ static struct bt_data sd[] = {
|
||||
|
||||
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,
|
||||
.interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
|
||||
.interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
|
||||
};
|
||||
|
||||
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;
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
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,
|
||||
const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
|
||||
{
|
||||
LOG_DBG("Received %u bytes", len);
|
||||
LOG_HEXDUMP_DBG(buf, len, "Data:");
|
||||
ARG_UNUSED(conn);
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
ARG_UNUSED(attr);
|
||||
LOG_INF("Notification state changed: 0x%04x", value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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));
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
struct bt_conn_info info;
|
||||
advertising_active = false;
|
||||
|
||||
int rc = bt_conn_get_info(conn, &info);
|
||||
if (rc == 0)
|
||||
{
|
||||
if (reserve_peer(conn) == NULL) {
|
||||
LOG_ERR("No free BLE peer slot available");
|
||||
return;
|
||||
}
|
||||
|
||||
rc = bt_conn_get_info(conn, &info);
|
||||
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", addr_str);
|
||||
LOG_INF("Role: %s", info.role == BT_CONN_ROLE_CENTRAL ? "Central" : "Peripheral");
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INF("Connected (info retrieval failed)");
|
||||
|
||||
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
||||
(void)start_advertising();
|
||||
}
|
||||
|
||||
event_mgmt_set_event(EVENT_MGMT_BLE_CONNECTED);
|
||||
}
|
||||
|
||||
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 */
|
||||
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);
|
||||
LOG_INF("Disconnected (reason 0x%02x, %u/%u active)", reason,
|
||||
(unsigned int)peer_count(), CONFIG_BT_MAX_CONN);
|
||||
|
||||
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
||||
(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);
|
||||
event_mgmt_set_event(EVENT_MGMT_BLE_DISCONNECTED);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.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 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);
|
||||
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);
|
||||
if (rc)
|
||||
{
|
||||
if (rc != 0) {
|
||||
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;
|
||||
LOG_INF("BLE init: set_device_name");
|
||||
set_device_name(name_to_use);
|
||||
LOG_INF("ble_mgmt_init: bt_enable done, marking ready");
|
||||
ble_ready = true;
|
||||
bt_gatt_cb_register(&gatt_callbacks);
|
||||
advertising_active = false;
|
||||
set_device_name(device_name);
|
||||
|
||||
LOG_INF("BLE init: bt_le_adv_start");
|
||||
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);
|
||||
LOG_INF("ble_mgmt_init: calling start_advertising");
|
||||
rc = start_advertising();
|
||||
if (rc != 0) {
|
||||
LOG_ERR("start_advertising failed (err %d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOG_INF("Bluetooth initialized. Adv-Name: %s", current_device_name);
|
||||
LOG_INF("ble_mgmt_init: complete");
|
||||
return 0;
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
#define EVENT_MGMT_AUDIO_READY BIT(1)
|
||||
#define EVENT_MGMT_BLE_CONNECTED BIT(2)
|
||||
#define EVENT_MGMT_BLE_DISCONNECTED BIT(3)
|
||||
#define EVENT_MGMT_AUDIO_ACTIVE BIT(4)
|
||||
#define EVENT_MGMT_AUDIO_IDLE BIT(5)
|
||||
|
||||
extern struct k_event event_mgmt_events;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ menuconfig FS_MGMT
|
||||
select FILE_SYSTEM_LITTLEFS
|
||||
select FILE_SYSTEM_MKFS
|
||||
select FLASH_PAGE_LAYOUT
|
||||
select SPI_NOR if BOARD_BUZZY
|
||||
select NORDIC_QSPI_NOR if BOARD_BUZZY
|
||||
select PM_OVERRIDE_EXTERNAL_DRIVER_CHECK if BOARD_BUZZY
|
||||
select NORDIC_QSPI_NOR if BOARD_NRF52840DK_NRF52840
|
||||
help
|
||||
|
||||
@@ -12,13 +12,16 @@
|
||||
|
||||
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)
|
||||
#elif DT_NODE_EXISTS(DT_NODELABEL(storage_partition))
|
||||
#define FS_PARTITION_ID FIXED_PARTITION_ID(storage_partition)
|
||||
#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
|
||||
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,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_PM_DEVICE) && !defined(CONFIG_FILE_SYSTEM_SHELL)
|
||||
static int open_count = 0;
|
||||
static struct k_mutex flash_pm_lock;
|
||||
#endif
|
||||
|
||||
// #define ACK_WATERMARK (CONFIG_BUZZ_PROTO_SLAB_COUNT / 4)
|
||||
#define INITIAL_CREDITS CONFIG_BUZZ_PROTO_SLAB_COUNT
|
||||
@@ -76,7 +81,9 @@ static struct
|
||||
*/
|
||||
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))
|
||||
{
|
||||
return -ENODEV;
|
||||
@@ -102,7 +109,7 @@ static int fs_mgmt_pm_flash_suspend(void)
|
||||
}
|
||||
|
||||
k_mutex_unlock(&flash_pm_lock);
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
#endif /* CONFIG_FILE_SYSTEM_SHELL / CONFIG_PM_DEVICE */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -113,7 +120,9 @@ static int fs_mgmt_pm_flash_suspend(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))
|
||||
return -ENODEV;
|
||||
|
||||
@@ -131,7 +140,7 @@ static int fs_mgmt_pm_flash_resume(void)
|
||||
open_count++;
|
||||
|
||||
k_mutex_unlock(&flash_pm_lock);
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
#endif /* CONFIG_FILE_SYSTEM_SHELL / CONFIG_PM_DEVICE */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -179,6 +188,20 @@ int fs_mgmt_pm_unlink(const char *path)
|
||||
{
|
||||
LOG_DBG("PM Unlinking file '%s'", path);
|
||||
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);
|
||||
fs_mgmt_pm_flash_suspend();
|
||||
return rc;
|
||||
@@ -397,7 +420,9 @@ int fs_mgmt_pm_mkdir_recursive(char *path)
|
||||
|
||||
static int fs_mgmt_init(void)
|
||||
{
|
||||
#if defined(CONFIG_PM_DEVICE) && !defined(CONFIG_FILE_SYSTEM_SHELL)
|
||||
k_mutex_init(&flash_pm_lock);
|
||||
#endif
|
||||
|
||||
if (!device_is_ready(flash_dev))
|
||||
{
|
||||
|
||||
5
firmware/libs/settings_mgmt/CMakeLists.txt
Normal file
5
firmware/libs/settings_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_SETTINGS_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_library_sources(src/settings_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
15
firmware/libs/settings_mgmt/Kconfig
Normal file
15
firmware/libs/settings_mgmt/Kconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
menuconfig SETTINGS_MGMT
|
||||
bool "Settings Management"
|
||||
default y
|
||||
select SETTINGS
|
||||
select SETTINGS_RUNTIME
|
||||
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
|
||||
34
firmware/libs/settings_mgmt/include/settings_mgmt.h
Normal file
34
firmware/libs/settings_mgmt/include/settings_mgmt.h
Normal 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
|
||||
243
firmware/libs/settings_mgmt/src/settings_mgmt.c
Normal file
243
firmware/libs/settings_mgmt/src/settings_mgmt.c
Normal file
@@ -0,0 +1,243 @@
|
||||
#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 = {
|
||||
#ifdef CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME
|
||||
.dev_name = CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME,
|
||||
#else
|
||||
.dev_name = CONFIG_BOARD, /* Default to board name if no explicit default set */
|
||||
#endif
|
||||
.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();
|
||||
}
|
||||
5
firmware/libs/usb_mgmt/CMakeLists.txt
Normal file
5
firmware/libs/usb_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_USB_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_library_sources(src/usb_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
13
firmware/libs/usb_mgmt/Kconfig
Normal file
13
firmware/libs/usb_mgmt/Kconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
menuconfig USB_MGMT
|
||||
bool "USB Management"
|
||||
default y
|
||||
select NRFX_POWER
|
||||
help
|
||||
Library for USB-related queries, e.g. VBUS presence detection
|
||||
via the nRF52840 POWER peripheral.
|
||||
|
||||
if USB_MGMT
|
||||
module = USB_MGMT
|
||||
module-str = usb_mgmt
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
endif # USB_MGMT
|
||||
30
firmware/libs/usb_mgmt/include/usb_mgmt.h
Normal file
30
firmware/libs/usb_mgmt/include/usb_mgmt.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef USB_MGMT_H
|
||||
#define USB_MGMT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define USB_MGMT_VBUS_CONNECTED BIT(0)
|
||||
#define USB_MGMT_VBUS_DISCONNECTED BIT(1)
|
||||
|
||||
extern struct k_event usb_mgmt_events;
|
||||
|
||||
/**
|
||||
* @brief Wait until usb_mgmt has completed initialization.
|
||||
*
|
||||
* @param timeout Timeout for waiting.
|
||||
*
|
||||
* @retval true usb_mgmt is ready.
|
||||
* @retval false timeout occurred.
|
||||
*/
|
||||
bool usb_mgmt_wait_ready(k_timeout_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Returns true when VBUS (USB 5 V) is present.
|
||||
*
|
||||
* Reads the USBREGSTATUS.VBUSDETECT bit in the nRF52840 POWER peripheral.
|
||||
* No USB stack needs to be enabled.
|
||||
*/
|
||||
bool usb_mgmt_is_vbus_present(void);
|
||||
|
||||
#endif /* USB_MGMT_H */
|
||||
65
firmware/libs/usb_mgmt/src/usb_mgmt.c
Normal file
65
firmware/libs/usb_mgmt/src/usb_mgmt.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <hal/nrf_power.h>
|
||||
#include <nrfx_power.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "usb_mgmt.h"
|
||||
|
||||
LOG_MODULE_REGISTER(usb_mgmt, CONFIG_USB_MGMT_LOG_LEVEL);
|
||||
|
||||
K_EVENT_DEFINE(usb_mgmt_events);
|
||||
K_SEM_DEFINE(usb_mgmt_ready_sem, 0, 1);
|
||||
|
||||
static bool usb_mgmt_last_vbus_state;
|
||||
static bool usb_mgmt_ready;
|
||||
|
||||
static void usb_mgmt_power_handler(nrfx_power_usb_evt_t event)
|
||||
{
|
||||
bool is_present = (event == NRFX_POWER_USB_EVT_DETECTED);
|
||||
|
||||
LOG_INF("VBUS %s (event=%u)", is_present ? "connected" : "disconnected", event);
|
||||
|
||||
if (is_present != usb_mgmt_last_vbus_state) {
|
||||
usb_mgmt_last_vbus_state = is_present;
|
||||
k_event_post(&usb_mgmt_events,
|
||||
is_present ? USB_MGMT_VBUS_CONNECTED : USB_MGMT_VBUS_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_mgmt_init(void)
|
||||
{
|
||||
usb_mgmt_last_vbus_state = nrf_power_usbregstatus_vbusdet_get(NRF_POWER);
|
||||
LOG_DBG("Boot VBUS state: %s", usb_mgmt_last_vbus_state ? "present" : "absent");
|
||||
k_event_post(&usb_mgmt_events,
|
||||
usb_mgmt_last_vbus_state ? USB_MGMT_VBUS_CONNECTED : USB_MGMT_VBUS_DISCONNECTED);
|
||||
|
||||
nrfx_power_config_t config = { 0 };
|
||||
nrfx_power_usbevt_config_t usb_config = {
|
||||
.handler = usb_mgmt_power_handler,
|
||||
};
|
||||
|
||||
nrfx_power_init(&config);
|
||||
nrfx_power_usbevt_init(&usb_config);
|
||||
nrfx_power_usbevt_enable();
|
||||
|
||||
usb_mgmt_ready = true;
|
||||
k_sem_give(&usb_mgmt_ready_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(usb_mgmt_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
||||
bool usb_mgmt_is_vbus_present(void)
|
||||
{
|
||||
return usb_mgmt_last_vbus_state;
|
||||
}
|
||||
|
||||
bool usb_mgmt_wait_ready(k_timeout_t timeout)
|
||||
{
|
||||
if (usb_mgmt_ready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return k_sem_take(&usb_mgmt_ready_sem, timeout) == 0;
|
||||
}
|
||||
2
firmware/output.txt
Normal file
2
firmware/output.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: Build directory /Users/edi/nrf_playground/buzzer_2/firmware/build targets board buzzy/nrf52840, but board {self.args.board} was specified. (Clean the directory, use --pristine, or use --build-dir to specify a different one.)
|
||||
FATAL ERROR: refusing to proceed without --force due to above error
|
||||
@@ -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:
|
||||
address: 0x0
|
||||
size: 0x800000
|
||||
|
||||
@@ -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
|
||||
@@ -1,6 +1,3 @@
|
||||
### Bluetooth
|
||||
CONFIG_BLE_MGMT=y
|
||||
|
||||
### Error handling
|
||||
CONFIG_HW_STACK_PROTECTION=y
|
||||
CONFIG_RESET_ON_FATAL_ERROR=y
|
||||
@@ -8,5 +5,9 @@ CONFIG_RESET_ON_FATAL_ERROR=y
|
||||
### Power management
|
||||
CONFIG_PM_DEVICE=y
|
||||
|
||||
### Boot banner
|
||||
CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="Edis Buzzer"
|
||||
|
||||
### Stack
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
CONFIG_BT_RX_STACK_SIZE=4096
|
||||
|
||||
5
firmware/rtt_script.jlink
Normal file
5
firmware/rtt_script.jlink
Normal file
@@ -0,0 +1,5 @@
|
||||
connect
|
||||
rtt start
|
||||
sleep 3000
|
||||
rtt stop
|
||||
exit
|
||||
@@ -2,13 +2,30 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fs_mgmt.h"
|
||||
#include "buzz_proto.h"
|
||||
#include "fw_mgmt.h"
|
||||
#include "settings_mgmt.h"
|
||||
#include "batt_mgmt.h"
|
||||
// #include "fw_mgmt.h"
|
||||
// #include "audio.h"
|
||||
|
||||
LOG_MODULE_REGISTER(main);
|
||||
|
||||
static const char *battery_state_to_str(batt_mgmt_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case BATT_STATE_DISCHARGING:
|
||||
return "discharging";
|
||||
case BATT_STATE_FULL:
|
||||
return "full";
|
||||
case BATT_STATE_CHARGING:
|
||||
return "charging";
|
||||
case BATT_STATE_ERROR:
|
||||
return "error";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_BLE_MGMT)
|
||||
#include "ble_mgmt.h"
|
||||
void ble_rx_cb(const uint8_t *data, uint16_t len)
|
||||
@@ -49,24 +66,36 @@ void ble_rx_cb(const uint8_t *data, uint16_t len)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
#if IS_ENABLED(CONFIG_BLE_MGMT)
|
||||
/* BLE-Subsystem initialisieren und RX-Callback registrieren */
|
||||
int rc = ble_mgmt_init(ble_rx_cb, CONFIG_BLE_MGMT_DEFAULT_DEVICE_NAME);
|
||||
int rc = ble_mgmt_init(ble_rx_cb, app_cfg.dev_name);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to initialize BLE management: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
LOG_WRN("After BLE init");
|
||||
#else
|
||||
LOG_WRN("BLE not enabled");
|
||||
#endif
|
||||
|
||||
LOG_WRN("Main park loop active");
|
||||
#if IS_ENABLED(CONFIG_LOG)
|
||||
#if IS_ENABLED(CONFIG_BATT_MGMT)
|
||||
k_sleep(K_SECONDS(1));
|
||||
batt_mgmt_info_t batt_info;
|
||||
int batt_rc = batt_mgmt_get_info(&batt_info);
|
||||
if (batt_rc == 0) {
|
||||
LOG_INF("Battery after 1s: %d mV, %u%%, level=%u, state=%s (%d)",
|
||||
batt_info.voltage_mv,
|
||||
batt_info.percent,
|
||||
batt_info.level,
|
||||
battery_state_to_str(batt_info.state),
|
||||
batt_info.state);
|
||||
} else {
|
||||
LOG_WRN("Battery info read failed: %d", batt_rc);
|
||||
}
|
||||
#endif // CONFIG_BATT_MGMT
|
||||
#endif // CONFIG_LOG
|
||||
|
||||
// k_sleep(K_MSEC(500));
|
||||
// LOG_INF("Playing test audio files...");
|
||||
// audio_queue_play("/lfs/sys/update", false);
|
||||
// audio_queue_play("/lfs/sys/confirm", false);
|
||||
for (;;) {
|
||||
int32_t rem_ms = k_sleep(K_FOREVER);
|
||||
LOG_WRN("main woke unexpectedly (remaining=%d ms)", rem_ms);
|
||||
|
||||
@@ -287,7 +287,14 @@ export function parseIncomingFrame(view: DataView, sender: FrameSender) {
|
||||
|
||||
case FRAME.ERROR:
|
||||
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);
|
||||
if (lsReject) {
|
||||
const currentReject = lsReject;
|
||||
@@ -361,6 +368,7 @@ export function buildFWInfoRequest(): ArrayBuffer {
|
||||
export function buildLSRequest(path: string): ArrayBuffer {
|
||||
const encoder = new TextEncoder();
|
||||
const pathBytes = encoder.encode(path);
|
||||
console.debug(`[Protocol] LS request for path: ${path}`);
|
||||
const buffer = new ArrayBuffer(4 + pathBytes.length);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
@@ -442,6 +450,7 @@ export function setFileGetResolver(
|
||||
export function buildFileGetRequest(path: string): ArrayBuffer {
|
||||
const encoder = new TextEncoder();
|
||||
const pathBytes = encoder.encode(path);
|
||||
console.debug(`[Protocol] FILE_GET request for path: ${path}`);
|
||||
const buffer = new ArrayBuffer(4 + pathBytes.length);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
@@ -458,6 +467,7 @@ export function buildFileGetRequest(path: string): ArrayBuffer {
|
||||
export function buildTagsGetRequest(path: string): ArrayBuffer {
|
||||
const encoder = new TextEncoder();
|
||||
const pathBytes = encoder.encode(path);
|
||||
console.debug(`[Protocol] TAGS_GET request for path: ${path}`);
|
||||
const buffer = new ArrayBuffer(4 + pathBytes.length);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
|
||||
@@ -34,12 +34,18 @@ export async function refreshRemote() {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
|
||||
const currentFsInfo = get(fsInfo);
|
||||
console.debug("[Sync] Remote FS info:", currentFsInfo);
|
||||
|
||||
// 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));
|
||||
|
||||
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);
|
||||
|
||||
// Dateien sofort im UI anzeigen, bevor die Tags geladen sind
|
||||
@@ -101,6 +107,7 @@ export async function refreshLocal() {
|
||||
export async function downloadSelectedFiles() {
|
||||
const files = get(buzzerAudioFiles).filter(f => f.selected);
|
||||
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
|
||||
console.debug(`[Sync] Download prefix: ${pathPrefix}`);
|
||||
|
||||
if (files.length === 0) {
|
||||
addToast("Keine Dateien zum Herunterladen ausgewählt.", "warning");
|
||||
@@ -181,6 +188,7 @@ export async function deleteSelectedLocalFiles() {
|
||||
export async function deleteSelectedRemoteFiles() {
|
||||
const files = get(buzzerAudioFiles).filter(f => f.selected);
|
||||
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
|
||||
console.debug(`[Sync] Delete prefix: ${pathPrefix}`);
|
||||
|
||||
if (files.length === 0) return;
|
||||
|
||||
@@ -208,6 +216,7 @@ export async function deleteSelectedRemoteFiles() {
|
||||
export async function uploadSelectedFiles() {
|
||||
const files = get(localAudioFiles).filter(f => f.selected);
|
||||
const pathPrefix = get(fsInfo)?.audioPath || "/lfs/a";
|
||||
console.debug(`[Sync] Upload prefix: ${pathPrefix}`);
|
||||
|
||||
if (files.length === 0) {
|
||||
addToast("Keine Dateien zum Hochladen ausgewählt.", "warning");
|
||||
|
||||
@@ -66,6 +66,8 @@ export async function fetchDirectory(path: string): Promise<any[]> {
|
||||
}
|
||||
isListing = true;
|
||||
|
||||
console.debug(`[Transport] fetchDirectory(${path})`);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// Dem Parser sagen, wen er bei Erfolg/Fehler anrufen soll
|
||||
setLsResolver(
|
||||
@@ -95,6 +97,8 @@ export async function getFile(path: string): Promise<boolean> {
|
||||
}
|
||||
isFileTransferring = true;
|
||||
|
||||
console.debug(`[Transport] getFile(${path})`);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
setFileGetResolver(
|
||||
(result: any) => { isFileTransferring = false; resolve(result.success); },
|
||||
@@ -116,6 +120,8 @@ export async function getTags(path: string): Promise<Blob> {
|
||||
}
|
||||
isFileTransferring = true;
|
||||
|
||||
console.debug(`[Transport] getTags(${path})`);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
setFileGetResolver(
|
||||
(result: any) => {
|
||||
|
||||
Reference in New Issue
Block a user