Compare commits
6 Commits
980e23e51a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 78f0bce5dd | |||
| e74437a846 | |||
| 2d3ea34603 | |||
| 87cba0b419 | |||
| 52bab32309 | |||
| dd51f45084 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -7,6 +7,13 @@
|
|||||||
"name": "Launch firmware/build_nrf52840dk_debug",
|
"name": "Launch firmware/build_nrf52840dk_debug",
|
||||||
"config": "${workspaceFolder}/firmware/build_nrf52840dk_debug",
|
"config": "${workspaceFolder}/firmware/build_nrf52840dk_debug",
|
||||||
"runToEntryPoint": "main"
|
"runToEntryPoint": "main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "nrf-connect",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch firmware/build_buzzy/firmware",
|
||||||
|
"config": "${workspaceFolder}/firmware/build_buzzy/firmware",
|
||||||
|
"runToEntryPoint": "main"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -8,6 +8,7 @@
|
|||||||
],
|
],
|
||||||
"cmake.sourceDirectory": "C:/Projekte/buzzer_2/firmware/libs/ble_mgmt",
|
"cmake.sourceDirectory": "C:/Projekte/buzzer_2/firmware/libs/ble_mgmt",
|
||||||
"nrf-connect.debugging.bindings": {
|
"nrf-connect.debugging.bindings": {
|
||||||
"${workspaceFolder}/firmware/build_nrf52840dk_debug": "Launch firmware/build_nrf52840dk_debug"
|
"${workspaceFolder}/firmware/build_nrf52840dk_debug": "Launch firmware/build_nrf52840dk_debug",
|
||||||
|
"${workspaceFolder}/firmware/build_buzzy/firmware": "Launch firmware/build_buzzy/firmware"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
26
.vscode/tasks.json
vendored
Normal file
26
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "MAC RTT Streamer",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"while true; do nc localhost 19021; sleep 0.2; done"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
VERSION_MAJOR = 0
|
VERSION_MAJOR = 0
|
||||||
VERSION_MINOR = 0
|
VERSION_MINOR = 0
|
||||||
PATCHLEVEL = 6
|
PATCHLEVEL = 77
|
||||||
VERSION_TWEAK = 0
|
VERSION_TWEAK = 0
|
||||||
#if (IS_ENABLED(CONFIG_LOG))
|
#if (IS_ENABLED(CONFIG_LOG))
|
||||||
EXTRAVERSION = debug
|
EXTRAVERSION = debug
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
# Keep SPI NOR page layout aligned with generated LittleFS block size (4KB).
|
# Keep QSPI NOR page layout aligned with generated LittleFS block size (4KB).
|
||||||
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
||||||
|
|||||||
@@ -18,47 +18,47 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
spi3_default: spi3_default {
|
// spi3_default: spi3_default {
|
||||||
group1 {
|
// group1 {
|
||||||
psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
// psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||||
<NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
// <NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||||
<NRF_PSEL(SPIM_MISO, 0, 30)>;
|
// <NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
spi3_sleep: spi3_sleep {
|
// spi3_sleep: spi3_sleep {
|
||||||
group1 {
|
// group1 {
|
||||||
psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
// psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
|
||||||
<NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
// <NRF_PSEL(SPIM_MOSI, 0, 29)>,
|
||||||
<NRF_PSEL(SPIM_MISO, 0, 30)>;
|
// <NRF_PSEL(SPIM_MISO, 0, 30)>;
|
||||||
low-power-enable;
|
// low-power-enable;
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optional future QSPI pinctrl states (keep disabled for now).
|
* Optional future QSPI pinctrl states (keep disabled for now).
|
||||||
* Use these when switching from &spi3 to &qspi in buzzy.dts.
|
* Use these when switching from &spi3 to &qspi in buzzy.dts.
|
||||||
*/
|
*/
|
||||||
// qspi_default: qspi_default {
|
qspi_default: qspi_default {
|
||||||
// group1 {
|
group1 {
|
||||||
// psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||||
// <NRF_PSEL(QSPI_CSN, 0, 5)>,
|
<NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||||
// <NRF_PSEL(QSPI_IO0, 0, 29)>,
|
<NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||||
// <NRF_PSEL(QSPI_IO1, 0, 30)>,
|
<NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||||
// <NRF_PSEL(QSPI_IO2, 0, 31)>,
|
<NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||||
// <NRF_PSEL(QSPI_IO3, 1, 13)>;
|
<NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||||
// };
|
};
|
||||||
// };
|
};
|
||||||
|
|
||||||
// qspi_sleep: qspi_sleep {
|
qspi_sleep: qspi_sleep {
|
||||||
// group1 {
|
group1 {
|
||||||
// psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
psels = <NRF_PSEL(QSPI_SCK, 0, 2)>,
|
||||||
// <NRF_PSEL(QSPI_CSN, 0, 5)>,
|
<NRF_PSEL(QSPI_CSN, 0, 5)>,
|
||||||
// <NRF_PSEL(QSPI_IO0, 0, 29)>,
|
<NRF_PSEL(QSPI_IO0, 0, 29)>,
|
||||||
// <NRF_PSEL(QSPI_IO1, 0, 30)>,
|
<NRF_PSEL(QSPI_IO1, 0, 30)>,
|
||||||
// <NRF_PSEL(QSPI_IO2, 0, 31)>,
|
<NRF_PSEL(QSPI_IO2, 0, 31)>,
|
||||||
// <NRF_PSEL(QSPI_IO3, 1, 13)>;
|
<NRF_PSEL(QSPI_IO3, 1, 13)>;
|
||||||
// low-power-enable;
|
low-power-enable;
|
||||||
// };
|
};
|
||||||
// };
|
};
|
||||||
};
|
};
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
charger_status {
|
charger_status {
|
||||||
compatible = "gpio-keys";
|
compatible = "gpio-keys";
|
||||||
chg_status: chg_status {
|
chg_status: chg_status {
|
||||||
gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||||
label = "ETA6003_CHG_STATUS";
|
label = "ETA6003_CHG_STATUS";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -137,17 +137,52 @@
|
|||||||
* SO/SIO1-> P0.30
|
* SO/SIO1-> P0.30
|
||||||
* CS -> P0.05
|
* 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";
|
status = "okay";
|
||||||
pinctrl-0 = <&spi3_default>;
|
pinctrl-0 = <&qspi_default>;
|
||||||
pinctrl-1 = <&spi3_sleep>;
|
pinctrl-1 = <&qspi_sleep>;
|
||||||
pinctrl-names = "default", "sleep";
|
pinctrl-names = "default", "sleep";
|
||||||
cs-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
|
||||||
|
|
||||||
mx25r64: flash@0 {
|
mx25r64: flash@0 {
|
||||||
compatible = "jedec,spi-nor";
|
compatible = "nordic,qspi-nor";
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
spi-max-frequency = <8000000>;
|
sck-frequency = <32000000>;
|
||||||
jedec-id = [c2 28 17];
|
jedec-id = [c2 28 17];
|
||||||
size = <DT_SIZE_M(64)>;
|
size = <DT_SIZE_M(64)>;
|
||||||
has-dpd;
|
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 {
|
&gpio0 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|||||||
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,6 +1,6 @@
|
|||||||
### Logging
|
### Logging
|
||||||
CONFIG_LOG=y
|
CONFIG_LOG=y
|
||||||
CONFIG_LOG_MODE_IMMEDIATE=y
|
#CONFIG_LOG_MODE_IMMEDIATE=y
|
||||||
CONFIG_DEBUG=y
|
CONFIG_DEBUG=y
|
||||||
|
|
||||||
CONFIG_DEBUG_OPTIMIZATIONS=y
|
CONFIG_DEBUG_OPTIMIZATIONS=y
|
||||||
@@ -8,16 +8,22 @@ 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
|
### Lib logging levels
|
||||||
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
|
# CONFIG_BATT_MGMT_LOG_LEVEL_DBG=y
|
||||||
CONFIG_LOG_BUFFER_SIZE=4096
|
# CONFIG_BATT_MGMT_BLINK_INTERVAL_LOGGING=y
|
||||||
|
# CONFIG_USB_MGMT_LOG_LEVEL_DBG=y
|
||||||
|
CONFIG_BUZZ_PROTO_LOG_LEVEL_DBG=y
|
||||||
|
|
||||||
### Bluetooth subsystem logging (reduced noise)
|
### Bluetooth
|
||||||
|
CONFIG_BLE_MGMT=y
|
||||||
CONFIG_BT_LOG_LEVEL_WRN=y
|
CONFIG_BT_LOG_LEVEL_WRN=y
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
CONFIG_BUZZ_AUDIO=n
|
||||||
|
|
||||||
### Shell features shared by all debug variants
|
### Shell features shared by all debug variants
|
||||||
CONFIG_SHELL=y
|
CONFIG_SHELL=y
|
||||||
CONFIG_SHELL_LOG_BACKEND=n
|
CONFIG_SHELL_LOG_BACKEND=y
|
||||||
CONFIG_FILE_SYSTEM_SHELL=y
|
CONFIG_FILE_SYSTEM_SHELL=y
|
||||||
CONFIG_SHELL_STACK_SIZE=2048
|
CONFIG_SHELL_STACK_SIZE=2048
|
||||||
CONFIG_FILE_SYSTEM_SHELL_LS_SIZE=y
|
CONFIG_FILE_SYSTEM_SHELL_LS_SIZE=y
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ add_subdirectory(ble_mgmt)
|
|||||||
add_subdirectory(buzz_proto)
|
add_subdirectory(buzz_proto)
|
||||||
add_subdirectory(audio)
|
add_subdirectory(audio)
|
||||||
add_subdirectory(event_mgmt)
|
add_subdirectory(event_mgmt)
|
||||||
|
add_subdirectory(batt_mgmt)
|
||||||
|
add_subdirectory(usb_mgmt)
|
||||||
@@ -5,3 +5,5 @@ 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"
|
||||||
|
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);
|
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)
|
if (old_state != new_state)
|
||||||
{
|
{
|
||||||
LOG_INF("Audio state %s -> %s (%s)",
|
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 = 0x00,
|
||||||
|
BATT_STATE_FULL= 0x01,
|
||||||
|
BATT_STATE_CHARGING = 0x02,
|
||||||
|
BATT_STATE_ERROR = 0x03,
|
||||||
|
BATT_STATE_UNKNOWN = 0x04,
|
||||||
|
} 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);
|
||||||
@@ -10,7 +10,7 @@ menuconfig BLE_MGMT
|
|||||||
if BLE_MGMT
|
if BLE_MGMT
|
||||||
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 2.0"
|
||||||
help
|
help
|
||||||
Device name used when ble_mgmt_init() is called with a NULL name.
|
Device name used when ble_mgmt_init() is called with a NULL name.
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
#include "ble_mgmt.h"
|
#include "ble_mgmt.h"
|
||||||
|
#include "event_mgmt.h"
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);
|
||||||
|
|
||||||
@@ -239,6 +240,8 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
||||||
(void)start_advertising();
|
(void)start_advertising();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_mgmt_set_event(EVENT_MGMT_BLE_CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||||
@@ -252,6 +255,8 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
|
|||||||
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
if (peer_count() < CONFIG_BT_MAX_CONN) {
|
||||||
(void)start_advertising();
|
(void)start_advertising();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_mgmt_set_event(EVENT_MGMT_BLE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ enum buzz_data_type
|
|||||||
BUZZ_DATA_DEVICE_INFO = 0x02,
|
BUZZ_DATA_DEVICE_INFO = 0x02,
|
||||||
BUZZ_DATA_FS_INFO = 0x03,
|
BUZZ_DATA_FS_INFO = 0x03,
|
||||||
BUZZ_DATA_FW_INFO = 0x04,
|
BUZZ_DATA_FW_INFO = 0x04,
|
||||||
|
BUZZ_DATA_BATT_INFO = 0x05,
|
||||||
|
|
||||||
BUZZ_DATA_FILE_GET = 0x20,
|
BUZZ_DATA_FILE_GET = 0x20,
|
||||||
BUZZ_DATA_FILE_PUT = 0x21,
|
BUZZ_DATA_FILE_PUT = 0x21,
|
||||||
@@ -122,6 +123,17 @@ struct __attribute__((packed)) buzz_resp_fw_info
|
|||||||
uint8_t kernel_version_length; /* Länge der Kernel-Versionszeichenkette */
|
uint8_t kernel_version_length; /* Länge der Kernel-Versionszeichenkette */
|
||||||
char data[]; /* Variabler String ohne Null-Terminierung: [fw_version][kernel_version] */
|
char data[]; /* Variabler String ohne Null-Terminierung: [fw_version][kernel_version] */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Payload für die Batterie-Infos */
|
||||||
|
struct __attribute__((packed)) buzz_resp_batt_info
|
||||||
|
{
|
||||||
|
uint8_t data_type; /* BUZZ_DATA_BATT_INFO */
|
||||||
|
uint8_t batt_status; /* batt_mgmt_state_t */
|
||||||
|
uint8_t batt_level; /* 0..4 */
|
||||||
|
uint8_t batt_percent; /* 0..100 */
|
||||||
|
uint16_t batt_voltage_mv; /* Little Endian */
|
||||||
|
};
|
||||||
|
|
||||||
/* Payload für das Entfernen einer Datei */
|
/* Payload für das Entfernen einer Datei */
|
||||||
struct __attribute__((packed)) buzz_rm_file_payload
|
struct __attribute__((packed)) buzz_rm_file_payload
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "buzz_proto.h"
|
#include "buzz_proto.h"
|
||||||
|
#include "batt_mgmt.h"
|
||||||
#include "fs_mgmt.h"
|
#include "fs_mgmt.h"
|
||||||
#include "fw_mgmt.h"
|
#include "fw_mgmt.h"
|
||||||
|
|
||||||
@@ -293,6 +294,43 @@ static void handle_fw_info_request(struct buzz_frame_msg *msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_batt_info_request(struct buzz_frame_msg *msg)
|
||||||
|
{
|
||||||
|
struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr;
|
||||||
|
struct buzz_resp_batt_info *resp_data = (struct buzz_resp_batt_info *)(msg->data_ptr + sizeof(*hdr));
|
||||||
|
batt_mgmt_info_t batt_info;
|
||||||
|
uint16_t voltage_mv = 0;
|
||||||
|
int rc = batt_mgmt_get_info(&batt_info);
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_WRN("Failed to get battery info: %d", rc);
|
||||||
|
send_error_frame(msg, abs(rc));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batt_info.voltage_mv > 0)
|
||||||
|
{
|
||||||
|
voltage_mv = (batt_info.voltage_mv > UINT16_MAX) ? UINT16_MAX : (uint16_t)batt_info.voltage_mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr->frame_type = BUZZ_FRAME_RESPONSE;
|
||||||
|
hdr->payload_length = sys_cpu_to_le16(sizeof(struct buzz_resp_batt_info));
|
||||||
|
|
||||||
|
resp_data->data_type = BUZZ_DATA_BATT_INFO;
|
||||||
|
resp_data->batt_status = (uint8_t)batt_info.state;
|
||||||
|
resp_data->batt_level = batt_info.level;
|
||||||
|
resp_data->batt_percent = batt_info.percent;
|
||||||
|
resp_data->batt_voltage_mv = sys_cpu_to_le16(voltage_mv);
|
||||||
|
|
||||||
|
if (msg->reply_cb)
|
||||||
|
{
|
||||||
|
msg->reply_cb(msg->data_ptr, sizeof(struct buzz_proto_header) + sizeof(struct buzz_resp_batt_info));
|
||||||
|
}
|
||||||
|
LOG_DBG("Battery Info sent: state=%u, level=%u, percent=%u, voltage=%u mV",
|
||||||
|
batt_info.state, batt_info.level, batt_info.percent, batt_info.voltage_mv);
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_ls_request(struct buzz_frame_msg *msg)
|
static void handle_ls_request(struct buzz_frame_msg *msg)
|
||||||
{
|
{
|
||||||
struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr;
|
struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr;
|
||||||
@@ -698,6 +736,11 @@ static void handle_request(struct buzz_frame_msg *msg)
|
|||||||
handle_fw_info_request(msg);
|
handle_fw_info_request(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BUZZ_DATA_BATT_INFO:
|
||||||
|
LOG_DBG("Received BATT Info Request");
|
||||||
|
handle_batt_info_request(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case BUZZ_DATA_FILE_GET:
|
case BUZZ_DATA_FILE_GET:
|
||||||
LOG_DBG("Received FILE_GET Request");
|
LOG_DBG("Received FILE_GET Request");
|
||||||
handle_file_get_request(msg, false);
|
handle_file_get_request(msg, false);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#define EVENT_MGMT_AUDIO_READY BIT(1)
|
#define EVENT_MGMT_AUDIO_READY BIT(1)
|
||||||
#define EVENT_MGMT_BLE_CONNECTED BIT(2)
|
#define EVENT_MGMT_BLE_CONNECTED BIT(2)
|
||||||
#define EVENT_MGMT_BLE_DISCONNECTED BIT(3)
|
#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;
|
extern struct k_event event_mgmt_events;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ menuconfig FS_MGMT
|
|||||||
select FILE_SYSTEM_LITTLEFS
|
select FILE_SYSTEM_LITTLEFS
|
||||||
select FILE_SYSTEM_MKFS
|
select FILE_SYSTEM_MKFS
|
||||||
select FLASH_PAGE_LAYOUT
|
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 PM_OVERRIDE_EXTERNAL_DRIVER_CHECK if BOARD_BUZZY
|
||||||
select NORDIC_QSPI_NOR if BOARD_NRF52840DK_NRF52840
|
select NORDIC_QSPI_NOR if BOARD_NRF52840DK_NRF52840
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ menuconfig SETTINGS_MGMT
|
|||||||
bool "Settings Management"
|
bool "Settings Management"
|
||||||
default y
|
default y
|
||||||
select SETTINGS
|
select SETTINGS
|
||||||
|
select SETTINGS_RUNTIME
|
||||||
select ZMS
|
select ZMS
|
||||||
help
|
help
|
||||||
Library for initializing and managing the settings subsystem.
|
Library for initializing and managing the settings subsystem.
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ LOG_MODULE_REGISTER(settings_mgmt, LOG_LEVEL_INF);
|
|||||||
|
|
||||||
/* The "Source of Truth" in RAM */
|
/* The "Source of Truth" in RAM */
|
||||||
struct app_settings_t app_cfg = {
|
struct app_settings_t app_cfg = {
|
||||||
.dev_name = "Edis Buzzer",
|
#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 % */
|
.vol = 100, /* 0-100 % */
|
||||||
.shuffle_mode = 0,
|
.shuffle_mode = 0,
|
||||||
.ble_timeout = 0xFFFFFFFF,
|
.ble_timeout = 0xFFFFFFFF,
|
||||||
|
|||||||
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,9 +1,3 @@
|
|||||||
### Bluetooth
|
|
||||||
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
|
||||||
@@ -11,6 +5,9 @@ CONFIG_RESET_ON_FATAL_ERROR=y
|
|||||||
### Power management
|
### Power management
|
||||||
CONFIG_PM_DEVICE=y
|
CONFIG_PM_DEVICE=y
|
||||||
|
|
||||||
|
### Boot banner
|
||||||
|
CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="Edis Buzzer"
|
||||||
|
|
||||||
### Stack
|
### Stack
|
||||||
CONFIG_MAIN_STACK_SIZE=2048
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
CONFIG_BT_RX_STACK_SIZE=4096
|
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
|
||||||
@@ -3,11 +3,29 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "buzz_proto.h"
|
#include "buzz_proto.h"
|
||||||
|
#include "settings_mgmt.h"
|
||||||
|
#include "batt_mgmt.h"
|
||||||
// #include "fw_mgmt.h"
|
// #include "fw_mgmt.h"
|
||||||
// #include "audio.h"
|
// #include "audio.h"
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(main);
|
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)
|
#if IS_ENABLED(CONFIG_BLE_MGMT)
|
||||||
#include "ble_mgmt.h"
|
#include "ble_mgmt.h"
|
||||||
void ble_rx_cb(const uint8_t *data, uint16_t len)
|
void ble_rx_cb(const uint8_t *data, uint16_t len)
|
||||||
@@ -48,24 +66,36 @@ void ble_rx_cb(const uint8_t *data, uint16_t len)
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_BLE_MGMT)
|
#if IS_ENABLED(CONFIG_BLE_MGMT)
|
||||||
/* BLE-Subsystem initialisieren und RX-Callback registrieren */
|
/* 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) {
|
if (rc < 0) {
|
||||||
LOG_ERR("Failed to initialize BLE management: %d", rc);
|
LOG_ERR("Failed to initialize BLE management: %d", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
LOG_WRN("After BLE init");
|
|
||||||
#else
|
#else
|
||||||
LOG_WRN("BLE not enabled");
|
LOG_WRN("BLE not enabled");
|
||||||
#endif
|
#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 (;;) {
|
for (;;) {
|
||||||
int32_t rem_ms = k_sleep(K_FOREVER);
|
int32_t rem_ms = k_sleep(K_FOREVER);
|
||||||
LOG_WRN("main woke unexpectedly (remaining=%d ms)", rem_ms);
|
LOG_WRN("main woke unexpectedly (remaining=%d ms)", rem_ms);
|
||||||
|
|||||||
16
protocol.md
16
protocol.md
@@ -92,6 +92,7 @@ Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollt
|
|||||||
| `0x02` | `DEVICE_INFO` | aktiv | Device-Infos (Board, Revision, SOC, ID) |
|
| `0x02` | `DEVICE_INFO` | aktiv | Device-Infos (Board, Revision, SOC, ID) |
|
||||||
| `0x03` | `FS_INFO` | aktiv | Dateisystem- und Pfadinfos |
|
| `0x03` | `FS_INFO` | aktiv | Dateisystem- und Pfadinfos |
|
||||||
| `0x04` | `FW_INFO` | aktiv | Info über Firmware-Status und -Version sowie Kernelversion |
|
| `0x04` | `FW_INFO` | aktiv | Info über Firmware-Status und -Version sowie Kernelversion |
|
||||||
|
| `0x05` | `BATT_INFO` | aktiv | Info über die Batterie |
|
||||||
| `0x20` | `FILE_GET` | aktiv | Datei vom Device streamen |
|
| `0x20` | `FILE_GET` | aktiv | Datei vom Device streamen |
|
||||||
| `0x21` | `FILE_PUT` | aktiv | Datei zum Device hochladen |
|
| `0x21` | `FILE_PUT` | aktiv | Datei zum Device hochladen |
|
||||||
| `0x22` | `TAGS_GET` | aktiv | nur Tag-Bereich streamen |
|
| `0x22` | `TAGS_GET` | aktiv | nur Tag-Bereich streamen |
|
||||||
@@ -238,6 +239,7 @@ Request: keine Zusatzdaten
|
|||||||
Response:
|
Response:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
uint8_t data_type; /* 0x04 */
|
||||||
uint8_t fw_status; /* 0x00: Confirmed, 0x01: Pending, 0x02: Testing, 0xFF: Unbekannt */
|
uint8_t fw_status; /* 0x00: Confirmed, 0x01: Pending, 0x02: Testing, 0xFF: Unbekannt */
|
||||||
uint32_t slot1_size; /* (LE) Grösse des Firmware Update Slots */
|
uint32_t slot1_size; /* (LE) Grösse des Firmware Update Slots */
|
||||||
uint8_t fw_version_len; /* Länge des Firmware-Versionsstring */
|
uint8_t fw_version_len; /* Länge des Firmware-Versionsstring */
|
||||||
@@ -247,6 +249,20 @@ uint8_t data[]; /* FW-Version und Kernelversion, ohne Nullterminier
|
|||||||
|
|
||||||
***Hinweis:*** in der Aktuellen implementierung werden die Versionen auf 32 Zeichen limitiert.
|
***Hinweis:*** in der Aktuellen implementierung werden die Versionen auf 32 Zeichen limitiert.
|
||||||
|
|
||||||
|
### `BATT_INFO` (`0x05`)
|
||||||
|
|
||||||
|
Request: keine Zusatzdaten
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint8_t data_type; /* 0x05 */
|
||||||
|
uint8_t batt_status; /* 0x00: Discharging, 0x01: Full, 0x02: Charging, 0x03: Error, 0x04: Unknown */
|
||||||
|
uint8_t batt_level; /* 0-4, Anzahl Striche für den Akku */
|
||||||
|
uint8_t batt_percent; /* Akku-Füllstand in Prozent */
|
||||||
|
uint16_t batt_voltage_mv; /* (LE) Batteriespannung in mV */
|
||||||
|
```
|
||||||
|
|
||||||
### `LS` (`0x40`)
|
### `LS` (`0x40`)
|
||||||
|
|
||||||
Request-Payload:
|
Request-Payload:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FlashUsage from "./FlashUsage.svelte";
|
import FlashUsage from "./FlashUsage.svelte";
|
||||||
import { deviceInfo, fwInfo } from "../lib/store";
|
import { battInfo, deviceInfo, fwInfo } from "../lib/store";
|
||||||
import { FW_STATUS } from "../lib/protocol/constants";
|
import { BATT_STATUS, FW_STATUS } from "../lib/protocol/constants";
|
||||||
import { tooltip } from "../lib/actions/tooltip";
|
import { tooltip } from "../lib/actions/tooltip";
|
||||||
import {
|
import {
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
@@ -13,6 +13,52 @@
|
|||||||
BatteryFullIcon,
|
BatteryFullIcon,
|
||||||
BatteryChargingIcon,
|
BatteryChargingIcon,
|
||||||
} from "phosphor-svelte";
|
} from "phosphor-svelte";
|
||||||
|
|
||||||
|
function clampBatteryLevel(level: number): number {
|
||||||
|
if (Number.isNaN(level)) return 0;
|
||||||
|
return Math.max(0, Math.min(4, Math.trunc(level)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$: resolvedBattIcon = (() => {
|
||||||
|
if (!$battInfo) return BatteryEmptyIcon;
|
||||||
|
if ($battInfo.battStatus === BATT_STATUS.CHARGING) return BatteryChargingIcon;
|
||||||
|
|
||||||
|
switch (clampBatteryLevel($battInfo.battLevel)) {
|
||||||
|
case 0:
|
||||||
|
return BatteryEmptyIcon;
|
||||||
|
case 1:
|
||||||
|
return BatteryLowIcon;
|
||||||
|
case 2:
|
||||||
|
return BatteryMediumIcon;
|
||||||
|
case 3:
|
||||||
|
return BatteryHighIcon;
|
||||||
|
default:
|
||||||
|
return BatteryFullIcon;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
$: battStatusText = (() => {
|
||||||
|
if (!$battInfo) return "unbekannt";
|
||||||
|
switch ($battInfo.battStatus) {
|
||||||
|
case BATT_STATUS.DISCHARGING:
|
||||||
|
return "Entladen";
|
||||||
|
case BATT_STATUS.FULL:
|
||||||
|
return "Voll";
|
||||||
|
case BATT_STATUS.CHARGING:
|
||||||
|
return "Laden";
|
||||||
|
case BATT_STATUS.ERROR:
|
||||||
|
return "Fehler";
|
||||||
|
default:
|
||||||
|
return "Unbekannt";
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
$: battIconClass =
|
||||||
|
$battInfo?.battStatus === BATT_STATUS.ERROR
|
||||||
|
? "w-5 h-5 text-red-500"
|
||||||
|
: $battInfo?.battStatus === BATT_STATUS.CHARGING
|
||||||
|
? "w-5 h-5 text-emerald-600"
|
||||||
|
: "w-5 h-5";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
@@ -127,7 +173,14 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="key">Batterie</td>
|
<td class="key">Batterie</td>
|
||||||
<td class="value flex items-center gap-2">
|
<td class="value flex items-center gap-2">
|
||||||
85% <BatteryChargingIcon weight="bold" class="w-5 h-5" /> 1200mAh
|
{#if $battInfo}
|
||||||
|
<span>{$battInfo.battPercent}%</span>
|
||||||
|
<svelte:component this={resolvedBattIcon} weight="bold" class={battIconClass} />
|
||||||
|
<span class="text-text-muted">{$battInfo.battVoltageMv} mV</span>
|
||||||
|
<span class="text-text-muted">({battStatusText})</span>
|
||||||
|
{:else}
|
||||||
|
unbekannt
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const DATA = {
|
|||||||
DEVICE_INFO: 0x02,
|
DEVICE_INFO: 0x02,
|
||||||
FS_INFO: 0x03,
|
FS_INFO: 0x03,
|
||||||
FW_INFO: 0x04,
|
FW_INFO: 0x04,
|
||||||
|
BATT_INFO: 0x05,
|
||||||
|
|
||||||
FILE_GET: 0x20,
|
FILE_GET: 0x20,
|
||||||
FILE_PUT: 0x21,
|
FILE_PUT: 0x21,
|
||||||
@@ -66,3 +67,11 @@ export const FW_STATUS = {
|
|||||||
TESTING: 0x02,
|
TESTING: 0x02,
|
||||||
UNKNOWN: 0xFF,
|
UNKNOWN: 0xFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const BATT_STATUS = {
|
||||||
|
DISCHARGING: 0x00,
|
||||||
|
FULL: 0x01,
|
||||||
|
CHARGING: 0x02,
|
||||||
|
ERROR: 0x03,
|
||||||
|
UNKNOWN: 0x04,
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FRAME, DATA, ZEPHYR_ERRORS } from './constants';
|
import { FRAME, DATA, ZEPHYR_ERRORS } from './constants';
|
||||||
import { protocolInfo, deviceInfo, fsInfo, transferStats, fwInfo, resetTransferStats, transferDetails } from '../store';
|
import { protocolInfo, deviceInfo, fsInfo, transferStats, fwInfo, battInfo, resetTransferStats, transferDetails } from '../store';
|
||||||
import { addToast } from '../toast';
|
import { addToast } from '../toast';
|
||||||
import { SETTINGS } from '../settings';
|
import { SETTINGS } from '../settings';
|
||||||
import { crc32 } from './crc32';
|
import { crc32 } from './crc32';
|
||||||
@@ -84,6 +84,18 @@ export function parseIncomingFrame(view: DataView, sender: FrameSender) {
|
|||||||
const fwVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11, fw_version_length));
|
const fwVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11, fw_version_length));
|
||||||
const kernelVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11 + fw_version_length, kernel_version_length));
|
const kernelVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11 + fw_version_length, kernel_version_length));
|
||||||
fwInfo.set({ fwStatus, slot1Size, fwVersion, kernelVersion });
|
fwInfo.set({ fwStatus, slot1Size, fwVersion, kernelVersion });
|
||||||
|
break;
|
||||||
|
case DATA.BATT_INFO:
|
||||||
|
if (payloadLength < 6) {
|
||||||
|
console.warn(`Invalid BATT_INFO payload length: ${payloadLength}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const battStatus = view.getUint8(4);
|
||||||
|
const battLevel = view.getUint8(5);
|
||||||
|
const battPercent = view.getUint8(6);
|
||||||
|
const battVoltageMv = view.getUint16(7, true);
|
||||||
|
battInfo.set({ battStatus, battLevel, battPercent, battVoltageMv });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -365,6 +377,17 @@ export function buildFWInfoRequest(): ArrayBuffer {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildBattInfoRequest(): ArrayBuffer {
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
|
||||||
|
view.setUint8(0, FRAME.REQUEST);
|
||||||
|
view.setUint16(1, 1, true);
|
||||||
|
view.setUint8(3, DATA.BATT_INFO);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const SETTINGS = {
|
|||||||
bluetooth: {
|
bluetooth: {
|
||||||
connectionTimeoutMs: 3000, // Timeout für den Verbindungsaufbau
|
connectionTimeoutMs: 3000, // Timeout für den Verbindungsaufbau
|
||||||
appleMaxInflight: 15, // iOS erlaubt nur wenige unbestätigte Nachrichten, daher begrenzen wir die Anzahl der gleichzeitig gesendeten Frames
|
appleMaxInflight: 15, // iOS erlaubt nur wenige unbestätigte Nachrichten, daher begrenzen wir die Anzahl der gleichzeitig gesendeten Frames
|
||||||
|
batteryPollIntervalMs: 60_000, // Intervall für periodische BATT_INFO-Abfragen
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
toastDurationMs: 5000,
|
toastDurationMs: 5000,
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ export interface FwInfo {
|
|||||||
kernelVersion: string;
|
kernelVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BattInfo {
|
||||||
|
battStatus: number;
|
||||||
|
battLevel: number;
|
||||||
|
battPercent: number;
|
||||||
|
battVoltageMv: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface StorageUsage {
|
export interface StorageUsage {
|
||||||
totalBytes: number;
|
totalBytes: number;
|
||||||
freeBytes: number;
|
freeBytes: number;
|
||||||
@@ -84,6 +91,7 @@ export const protocolInfo = writable<ProtocolInfo | null>(null);
|
|||||||
export const deviceInfo = writable<DeviceInfo | null>(null);
|
export const deviceInfo = writable<DeviceInfo | null>(null);
|
||||||
export const fsInfo = writable<FsInfo | null>(null);
|
export const fsInfo = writable<FsInfo | null>(null);
|
||||||
export const fwInfo = writable<FwInfo | null>(null);
|
export const fwInfo = writable<FwInfo | null>(null);
|
||||||
|
export const battInfo = writable<BattInfo | null>(null);
|
||||||
|
|
||||||
// Dateilisten
|
// Dateilisten
|
||||||
export const buzzerAudioFiles = writable<BuzzerFile[]>([]);
|
export const buzzerAudioFiles = writable<BuzzerFile[]>([]);
|
||||||
@@ -277,6 +285,7 @@ export function resetRemote(): void {
|
|||||||
deviceInfo.set(null);
|
deviceInfo.set(null);
|
||||||
fsInfo.set(null);
|
fsInfo.set(null);
|
||||||
fwInfo.set(null);
|
fwInfo.set(null);
|
||||||
|
battInfo.set(null);
|
||||||
activeDeviceId.set(null);
|
activeDeviceId.set(null);
|
||||||
buzzerAudioFiles.set([]);
|
buzzerAudioFiles.set([]);
|
||||||
buzzerSysFiles.set([]);
|
buzzerSysFiles.set([]);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { isConnected, deviceInfo, fsInfo, fwInfo, buzzerAudioFiles, buzzerSysFiles, isTransferingRemote, isFetchingLocal, storageUsage, localAudioFiles, transferStats } from './store';
|
import { isConnected, deviceInfo, fsInfo, fwInfo, buzzerAudioFiles, buzzerSysFiles, isTransferingRemote, isFetchingLocal, storageUsage, localAudioFiles, transferStats } from './store';
|
||||||
import { requestProtocolInfo, requestFSInfo, fetchDirectory, getFile, putFile, deleteRemoteFile, requestDeviceInfo, requestFWInfo } from './transport';
|
import { requestProtocolInfo, requestFSInfo, fetchDirectory, getFile, putFile, deleteRemoteFile, requestDeviceInfo, requestFWInfo, requestBattInfo } from './transport';
|
||||||
import type { BuzzerFile } from './types';
|
import type { BuzzerFile } from './types';
|
||||||
import { addToast } from './toast';
|
import { addToast } from './toast';
|
||||||
import { getLocalFiles, deleteLocalFile, getLocalFile } from './db';
|
import { getLocalFiles, deleteLocalFile, getLocalFile } from './db';
|
||||||
@@ -28,6 +28,7 @@ export async function refreshRemote() {
|
|||||||
await requestProtocolInfo();
|
await requestProtocolInfo();
|
||||||
await requestFSInfo();
|
await requestFSInfo();
|
||||||
await requestFWInfo();
|
await requestFWInfo();
|
||||||
|
await requestBattInfo();
|
||||||
await requestDeviceInfo();
|
await requestDeviceInfo();
|
||||||
|
|
||||||
// Kurze Verzögerung für Store-Propagation
|
// Kurze Verzögerung für Store-Propagation
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { buildLSRequest, buildProtocolInfoRequest, buildDeviceInfoRequest, buildFSInfoRequest, buildFWInfoRequest, setLsResolver, buildFileGetRequest, setFileGetResolver, buildTagsGetRequest, uploadState } from './protocol/parser';
|
import { buildLSRequest, buildProtocolInfoRequest, buildDeviceInfoRequest, buildFSInfoRequest, buildFWInfoRequest, buildBattInfoRequest, setLsResolver, buildFileGetRequest, setFileGetResolver, buildTagsGetRequest, uploadState } from './protocol/parser';
|
||||||
import { crc32 } from './protocol/crc32';
|
import { crc32 } from './protocol/crc32';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { protocolInfo, transferStats, } from './store';
|
import { protocolInfo, transferStats, } from './store';
|
||||||
@@ -10,9 +10,45 @@ const isMac = navigator.userAgent.includes('Macintosh') || navigator.userAgent.i
|
|||||||
const MAX_INFLIGHT = isMac ? SETTINGS.bluetooth.appleMaxInflight : Infinity; // iOS erlaubt nur wenige unbestätigte Nachrichten
|
const MAX_INFLIGHT = isMac ? SETTINGS.bluetooth.appleMaxInflight : Infinity; // iOS erlaubt nur wenige unbestätigte Nachrichten
|
||||||
|
|
||||||
console.log("Transport: Max Inflight Frames =", MAX_INFLIGHT);
|
console.log("Transport: Max Inflight Frames =", MAX_INFLIGHT);
|
||||||
|
const BATT_POLL_INTERVAL_MS = Math.max(1_000, SETTINGS.bluetooth.batteryPollIntervalMs);
|
||||||
|
|
||||||
export type FrameSender = (buffer: ArrayBuffer) => Promise<void>;
|
export type FrameSender = (buffer: ArrayBuffer) => Promise<void>;
|
||||||
let currentSender: FrameSender | null = null;
|
let currentSender: FrameSender | null = null;
|
||||||
|
let battPollTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
|
let isBattPollInFlight = false;
|
||||||
|
|
||||||
|
function stopBattPolling() {
|
||||||
|
if (battPollTimer) {
|
||||||
|
clearInterval(battPollTimer);
|
||||||
|
battPollTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldSkipBattPoll(): boolean {
|
||||||
|
return isListing || isFileTransferring || uploadState.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollBatteryInfo() {
|
||||||
|
if (!currentSender || isBattPollInFlight || shouldSkipBattPoll()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBattPollInFlight = true;
|
||||||
|
try {
|
||||||
|
await requestBattInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.debug("Periodic BATT_INFO request failed:", error);
|
||||||
|
} finally {
|
||||||
|
isBattPollInFlight = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startBattPolling() {
|
||||||
|
stopBattPolling();
|
||||||
|
battPollTimer = setInterval(() => {
|
||||||
|
void pollBatteryInfo();
|
||||||
|
}, BATT_POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
export function registerTransport(sender: FrameSender | null) {
|
export function registerTransport(sender: FrameSender | null) {
|
||||||
currentSender = sender;
|
currentSender = sender;
|
||||||
@@ -21,6 +57,7 @@ export function registerTransport(sender: FrameSender | null) {
|
|||||||
// NEU: Wird von bluetooth.ts oder serial.ts nach dem physischen Connect gerufen
|
// NEU: Wird von bluetooth.ts oder serial.ts nach dem physischen Connect gerufen
|
||||||
export async function handleTransportConnect(sender: FrameSender) {
|
export async function handleTransportConnect(sender: FrameSender) {
|
||||||
registerTransport(sender);
|
registerTransport(sender);
|
||||||
|
stopBattPolling();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Basis-Informationen zwingend vorab laden
|
// Basis-Informationen zwingend vorab laden
|
||||||
@@ -28,9 +65,11 @@ export async function handleTransportConnect(sender: FrameSender) {
|
|||||||
await requestFSInfo();
|
await requestFSInfo();
|
||||||
await requestDeviceInfo();
|
await requestDeviceInfo();
|
||||||
await requestFWInfo();
|
await requestFWInfo();
|
||||||
|
await requestBattInfo();
|
||||||
|
|
||||||
// Erst wenn diese Basisdaten da sind, wird die UI freigeschaltet
|
// Erst wenn diese Basisdaten da sind, wird die UI freigeschaltet
|
||||||
isConnected.set(true);
|
isConnected.set(true);
|
||||||
|
startBattPolling();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Transport-Initialisierung fehlgeschlagen:", error);
|
console.error("Transport-Initialisierung fehlgeschlagen:", error);
|
||||||
handleTransportDisconnect();
|
handleTransportDisconnect();
|
||||||
@@ -58,6 +97,10 @@ export async function requestFWInfo() {
|
|||||||
await sendFrame(buildFWInfoRequest());
|
await sendFrame(buildFWInfoRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function requestBattInfo() {
|
||||||
|
await sendFrame(buildBattInfoRequest());
|
||||||
|
}
|
||||||
|
|
||||||
let isListing = false;
|
let isListing = false;
|
||||||
|
|
||||||
export async function fetchDirectory(path: string): Promise<any[]> {
|
export async function fetchDirectory(path: string): Promise<any[]> {
|
||||||
@@ -85,6 +128,7 @@ export async function fetchDirectory(path: string): Promise<any[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function handleTransportDisconnect() {
|
export function handleTransportDisconnect() {
|
||||||
|
stopBattPolling();
|
||||||
registerTransport(null);
|
registerTransport(null);
|
||||||
resetRemote();
|
resetRemote();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user