Merge remote-tracking branch 'origin/release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2023-05-17 18:28:55 +09:00
commit 16b976478c
273 changed files with 18819 additions and 3609 deletions

2
.github/CODEOWNERS vendored
View File

@ -56,7 +56,7 @@
# Lib # Lib
/lib/ST25RFAL002/ @skotopes @DrZlo13 @hedger @gornekich /lib/ST25RFAL002/ @skotopes @DrZlo13 @hedger @gornekich
/lib/STM32CubeWB/ @skotopes @DrZlo13 @hedger @gornekich /lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gornekich
/lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich /lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich
/lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov /lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
/lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov /lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov

View File

@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -19,7 +18,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -61,7 +60,7 @@ jobs:
- name: 'Bundle scripts' - name: 'Bundle scripts'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts
- name: 'Build the firmware' - name: 'Build the firmware'
run: | run: |
@ -167,7 +166,7 @@ jobs:
target: [f7, f18] target: [f7, f18]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -194,12 +193,14 @@ jobs:
TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \
./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package
echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT
echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT
- name: Deploy uFBT with SDK - name: Deploy uFBT with SDK
uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 uses: flipperdevices/flipperzero-ufbt-action@v0.1.0
with: with:
task: setup task: setup
sdk-file: ${{ steps.build-fw.outputs.sdk-file }} sdk-file: ${{ steps.build-fw.outputs.sdk-file }}
sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }}
- name: Build test app with SDK - name: Build test app with SDK
run: | run: |
@ -207,7 +208,7 @@ jobs:
cd testapp cd testapp
ufbt create APPID=testapp ufbt create APPID=testapp
ufbt ufbt
- name: Build example & external apps with uFBT - name: Build example & external apps with uFBT
run: | run: |
for appdir in 'applications/external' 'applications/examples'; do for appdir in 'applications/external' 'applications/examples'; do

View File

@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -19,7 +18,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -64,7 +63,7 @@ jobs:
else else
echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY;
fi fi
- name: 'Check C++ code formatting' - name: 'Check C++ code formatting'
id: syntax_check_cpp id: syntax_check_cpp
if: always() if: always()

View File

@ -13,7 +13,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -20,7 +19,7 @@ jobs:
runs-on: [self-hosted, FlipperZeroShell] runs-on: [self-hosted, FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@ -13,7 +13,7 @@ jobs:
runs-on: [self-hosted, FlipperZeroUnitTest] runs-on: [self-hosted, FlipperZeroUnitTest]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@ -13,13 +13,13 @@ jobs:
runs-on: [self-hosted, FlipperZeroUpdaterTest] runs-on: [self-hosted, FlipperZeroUpdaterTest]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 1 fetch-depth: 1
submodules: false submodules: false
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: 'Get flipper from device manager (mock)' - name: 'Get flipper from device manager (mock)'
@ -50,7 +50,7 @@ jobs:
echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout latest release' - name: 'Checkout latest release'
uses: actions/checkout@v3 uses: actions/checkout@v3

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.swp *.swp
*.swo *.swo
*.gdb_history *.gdb_history
*.old
# LSP # LSP

12
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "lib/mlib"] [submodule "lib/mlib"]
path = lib/mlib path = lib/mlib
url = https://github.com/P-p-H-d/mlib.git url = https://github.com/P-p-H-d/mlib.git
[submodule "lib/STM32CubeWB"]
path = lib/STM32CubeWB
url = https://github.com/Flipper-Zero/STM32CubeWB.git
[submodule "lib/littlefs"] [submodule "lib/littlefs"]
path = lib/littlefs path = lib/littlefs
url = https://github.com/littlefs-project/littlefs.git url = https://github.com/littlefs-project/littlefs.git
@ -34,3 +31,12 @@
[submodule "lib/heatshrink"] [submodule "lib/heatshrink"]
path = lib/heatshrink path = lib/heatshrink
url = https://github.com/flipperdevices/heatshrink.git url = https://github.com/flipperdevices/heatshrink.git
[submodule "lib/st_cmsis_device_wb"]
path = lib/stm32wb_cmsis
url = https://github.com/STMicroelectronics/cmsis_device_wb
[submodule "lib/stm32wbxx_hal_driver"]
path = lib/stm32wb_hal
url = https://github.com/STMicroelectronics/stm32wbxx_hal_driver
[submodule "lib/stm32wb_copro"]
path = lib/stm32wb_copro
url = https://github.com/flipperdevices/stm32wb_copro.git

View File

@ -1 +1 @@
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap

View File

@ -11,11 +11,10 @@
"args": { "args": {
"useSingleResult": true, "useSingleResult": true,
"env": { "env": {
"PATH": "${workspaceFolder};${env:PATH}", "PATH": "${workspaceFolder}${command:extension.commandvariable.envListSep}${env:PATH}"
"FBT_QUIET": 1
}, },
"command": "fbt get_blackmagic", "command": "fbt -s get_blackmagic",
"description": "Get Blackmagic device", "description": "Get Blackmagic device"
} }
} }
], ],
@ -28,20 +27,21 @@
"type": "cortex-debug", "type": "cortex-debug",
"servertype": "openocd", "servertype": "openocd",
"device": "stlink", "device": "stlink",
"svdFile": "./debug/STM32WB55_CM4.svd", "svdFile": "./scripts/debug/STM32WB55_CM4.svd",
// If you're debugging early in the boot process, before OS scheduler is running, // If you're debugging early in the boot process, before OS scheduler is running,
// you have to comment out the following line. // you have to comment out the following line.
"rtos": "FreeRTOS", "rtos": "FreeRTOS",
"configFiles": [ "configFiles": [
"interface/stlink.cfg", "interface/stlink.cfg",
"./debug/stm32wbx.cfg", "./scripts/debug/stm32wbx.cfg",
], ],
"postAttachCommands": [ "postAttachCommands": [
"source scripts/debug/flipperversion.py",
"fw-version",
// "compare-sections", // "compare-sections",
"source debug/flipperapps.py", "source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps", "fap-set-debug-elf-root build/latest/.extapps",
// "source debug/FreeRTOS/FreeRTOS.py", // "source scripts/debug/FreeRTOS/FreeRTOS.py",
// "svd_load debug/STM32WB55_CM4.svd"
] ]
// "showDevDebugOutput": "raw", // "showDevDebugOutput": "raw",
}, },
@ -53,14 +53,16 @@
"type": "cortex-debug", "type": "cortex-debug",
"servertype": "external", "servertype": "external",
"gdbTarget": "${input:BLACKMAGIC}", "gdbTarget": "${input:BLACKMAGIC}",
"svdFile": "./debug/STM32WB55_CM4.svd", "svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS", "rtos": "FreeRTOS",
"postAttachCommands": [ "postAttachCommands": [
"monitor swdp_scan", "monitor swdp_scan",
"attach 1", "attach 1",
"set confirm off", "set confirm off",
"set mem inaccessible-by-default off", "set mem inaccessible-by-default off",
"source debug/flipperapps.py", "source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps", "fap-set-debug-elf-root build/latest/.extapps",
// "compare-sections", // "compare-sections",
] ]
@ -75,10 +77,12 @@
"servertype": "jlink", "servertype": "jlink",
"interface": "swd", "interface": "swd",
"device": "STM32WB55RG", "device": "STM32WB55RG",
"svdFile": "./debug/STM32WB55_CM4.svd", "svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS", "rtos": "FreeRTOS",
"postAttachCommands": [ "postAttachCommands": [
"source debug/flipperapps.py", "source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps", "fap-set-debug-elf-root build/latest/.extapps",
] ]
// "showDevDebugOutput": "raw", // "showDevDebugOutput": "raw",
@ -91,27 +95,20 @@
"type": "cortex-debug", "type": "cortex-debug",
"servertype": "openocd", "servertype": "openocd",
"device": "cmsis-dap", "device": "cmsis-dap",
"svdFile": "./debug/STM32WB55_CM4.svd", "svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS", "rtos": "FreeRTOS",
"configFiles": [ "configFiles": [
"interface/cmsis-dap.cfg", "interface/cmsis-dap.cfg",
"./debug/stm32wbx.cfg", "./scripts/debug/stm32wbx.cfg",
], ],
"postAttachCommands": [ "postAttachCommands": [
"source debug/flipperapps.py", "source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps", "fap-set-debug-elf-root build/latest/.extapps",
], ],
// "showDevDebugOutput": "raw", // "showDevDebugOutput": "raw",
}, },
{
"name": "fbt debug",
"type": "python",
"request": "launch",
"program": "./lib/scons/scripts/scons.py",
"args": [
"plugin_dist"
]
},
{ {
"name": "python debug", "name": "python debug",
"type": "python", "type": "python",

View File

@ -8,11 +8,12 @@
"amiralizadeh9480.cpp-helper", "amiralizadeh9480.cpp-helper",
"marus25.cortex-debug", "marus25.cortex-debug",
"zxh404.vscode-proto3", "zxh404.vscode-proto3",
"augustocdias.tasks-shell-input" "augustocdias.tasks-shell-input",
"rioj7.command-variable"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [ "unwantedRecommendations": [
"twxs.cmake", "twxs.cmake",
"ms-vscode.cmake-tools" "ms-vscode.cmake-tools"
] ]
} }

View File

@ -99,7 +99,6 @@ Make sure your Flipper is on, and your firmware is functioning. Connect your Fli
- `applications` - applications and services used in firmware - `applications` - applications and services used in firmware
- `assets` - assets used by applications and services - `assets` - assets used by applications and services
- `furi` - Furi Core: OS-level primitives and helpers - `furi` - Furi Core: OS-level primitives and helpers
- `debug` - debug tool: GDB plugins, an SVD file, etc.
- `documentation` - documentation generation system configs and input files - `documentation` - documentation generation system configs and input files
- `firmware` - firmware source code - `firmware` - firmware source code
- `lib` - our and 3rd party libraries, drivers, etc. - `lib` - our and 3rd party libraries, drivers, etc.

View File

@ -239,19 +239,31 @@ distenv.PhonyTarget(
) )
# Debug alien elf # Debug alien elf
debug_other_opts = [
"-ex",
"source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py",
# "-ex",
# "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py",
"-ex",
"source ${FBT_DEBUG_DIR}/flipperversion.py",
"-ex",
"fw-version",
]
distenv.PhonyTarget( distenv.PhonyTarget(
"debug_other", "debug_other",
"${GDBPYCOM}", "${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE}", GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}", GDBREMOTE="${OPENOCD_GDB_PIPE}",
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', GDBPYOPTS=debug_other_opts,
) )
distenv.PhonyTarget( distenv.PhonyTarget(
"debug_other_blackmagic", "debug_other_blackmagic",
"${GDBPYCOM}", "${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="$${BLACKMAGIC_ADDR}", GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBPYOPTS=debug_other_opts,
) )
@ -335,3 +347,9 @@ vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
distenv.Precious(vscode_dist) distenv.Precious(vscode_dist)
distenv.NoClean(vscode_dist) distenv.NoClean(vscode_dist)
distenv.Alias("vscode_dist", vscode_dist) distenv.Alias("vscode_dist", vscode_dist)
# Configure shell with build tools
distenv.PhonyTarget(
"env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
)

View File

@ -407,7 +407,7 @@ MU_TEST(subghz_decoder_ido_test) {
"Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_keelog_test) { MU_TEST(subghz_decoder_keeloq_test) {
mu_assert( mu_assert(
subghz_decoder_test( subghz_decoder_test(
EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME),
@ -676,7 +676,7 @@ MU_TEST(subghz_encoder_nice_flo_test) {
"Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_keelog_test) { MU_TEST(subghz_encoder_keeloq_test) {
mu_assert( mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")),
"Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
@ -813,7 +813,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_gate_tx_test); MU_RUN_TEST(subghz_decoder_gate_tx_test);
MU_RUN_TEST(subghz_decoder_hormann_hsm_test); MU_RUN_TEST(subghz_decoder_hormann_hsm_test);
MU_RUN_TEST(subghz_decoder_ido_test); MU_RUN_TEST(subghz_decoder_ido_test);
MU_RUN_TEST(subghz_decoder_keelog_test); MU_RUN_TEST(subghz_decoder_keeloq_test);
MU_RUN_TEST(subghz_decoder_kia_seed_test); MU_RUN_TEST(subghz_decoder_kia_seed_test);
MU_RUN_TEST(subghz_decoder_nero_radio_test); MU_RUN_TEST(subghz_decoder_nero_radio_test);
MU_RUN_TEST(subghz_decoder_nero_sketch_test); MU_RUN_TEST(subghz_decoder_nero_sketch_test);
@ -852,7 +852,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_came_twee_test); MU_RUN_TEST(subghz_encoder_came_twee_test);
MU_RUN_TEST(subghz_encoder_gate_tx_test); MU_RUN_TEST(subghz_encoder_gate_tx_test);
MU_RUN_TEST(subghz_encoder_nice_flo_test); MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_encoder_keeloq_test);
MU_RUN_TEST(subghz_encoder_linear_test); MU_RUN_TEST(subghz_encoder_linear_test);
MU_RUN_TEST(subghz_encoder_linear_delta3_test); MU_RUN_TEST(subghz_encoder_linear_delta3_test);
MU_RUN_TEST(subghz_encoder_megacode_test); MU_RUN_TEST(subghz_encoder_megacode_test);

View File

@ -11,6 +11,7 @@ enum HidDebugSubmenuIndex {
HidSubmenuIndexMedia, HidSubmenuIndexMedia,
HidSubmenuIndexTikTok, HidSubmenuIndexTikTok,
HidSubmenuIndexMouse, HidSubmenuIndexMouse,
HidSubmenuIndexMouseClicker,
HidSubmenuIndexMouseJiggler, HidSubmenuIndexMouseJiggler,
}; };
@ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
} else if(index == HidSubmenuIndexTikTok) { } else if(index == HidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok; app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
} else if(index == HidSubmenuIndexMouseClicker) {
app->view_id = HidViewMouseClicker;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
} else if(index == HidSubmenuIndexMouseJiggler) { } else if(index == HidSubmenuIndexMouseJiggler) {
app->view_id = HidViewMouseJiggler; app->view_id = HidViewMouseJiggler;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
@ -53,6 +57,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_media_set_connected_status(hid->hid_media, connected); hid_media_set_connected_status(hid->hid_media, connected);
hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected);
hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
hid_tiktok_set_connected_status(hid->hid_tiktok, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
} }
@ -114,6 +119,12 @@ Hid* hid_alloc(HidTransport transport) {
hid_submenu_callback, hid_submenu_callback,
app); app);
} }
submenu_add_item(
app->device_type_submenu,
"Mouse Clicker",
HidSubmenuIndexMouseClicker,
hid_submenu_callback,
app);
submenu_add_item( submenu_add_item(
app->device_type_submenu, app->device_type_submenu,
"Mouse Jiggler", "Mouse Jiggler",
@ -172,6 +183,15 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
// Mouse clicker view
app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);
view_set_previous_callback(
hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher,
HidViewMouseClicker,
hid_mouse_clicker_get_view(app->hid_mouse_clicker));
// Mouse jiggler view // Mouse jiggler view
app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
view_set_previous_callback( view_set_previous_callback(
@ -205,6 +225,8 @@ void hid_free(Hid* app) {
hid_media_free(app->hid_media); hid_media_free(app->hid_media);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
hid_mouse_free(app->hid_mouse); hid_mouse_free(app->hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);
hid_mouse_clicker_free(app->hid_mouse_clicker);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
hid_mouse_jiggler_free(app->hid_mouse_jiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);

View File

@ -20,6 +20,7 @@
#include "views/hid_keyboard.h" #include "views/hid_keyboard.h"
#include "views/hid_media.h" #include "views/hid_media.h"
#include "views/hid_mouse.h" #include "views/hid_mouse.h"
#include "views/hid_mouse_clicker.h"
#include "views/hid_mouse_jiggler.h" #include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h" #include "views/hid_tiktok.h"
@ -43,6 +44,7 @@ struct Hid {
HidKeyboard* hid_keyboard; HidKeyboard* hid_keyboard;
HidMedia* hid_media; HidMedia* hid_media;
HidMouse* hid_mouse; HidMouse* hid_mouse;
HidMouseClicker* hid_mouse_clicker;
HidMouseJiggler* hid_mouse_jiggler; HidMouseJiggler* hid_mouse_jiggler;
HidTikTok* hid_tiktok; HidTikTok* hid_tiktok;

View File

@ -4,6 +4,7 @@ typedef enum {
HidViewKeyboard, HidViewKeyboard,
HidViewMedia, HidViewMedia,
HidViewMouse, HidViewMouse,
HidViewMouseClicker,
HidViewMouseJiggler, HidViewMouseJiggler,
BtHidViewTikTok, BtHidViewTikTok,
HidViewExitConfirm, HidViewExitConfirm,

View File

@ -0,0 +1,214 @@
#include "hid_mouse_clicker.h"
#include <gui/elements.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidMouseClicker"
#define DEFAULT_CLICK_RATE 1
#define MAXIMUM_CLICK_RATE 60
struct HidMouseClicker {
View* view;
Hid* hid;
FuriTimer* timer;
};
typedef struct {
bool connected;
bool running;
int rate;
HidTransport transport;
} HidMouseClickerModel;
static void hid_mouse_clicker_start_or_restart_timer(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
if(furi_timer_is_running(hid_mouse_clicker->timer)) {
furi_timer_stop(hid_mouse_clicker->timer);
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
furi_timer_start(
hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate);
},
true);
}
static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseClickerModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker");
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
if(model->running) {
canvas_set_font(canvas, FontPrimary);
FuriString* rate_label = furi_string_alloc();
furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate);
elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label));
canvas_set_font(canvas, FontSecondary);
furi_string_free(rate_label);
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking");
canvas_set_font(canvas, FontSecondary);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
if(model->running) {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
} else {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_clicker_timer_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
if(model->running) {
hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
}
},
false);
}
static void hid_mouse_clicker_enter_callback(void* context) {
hid_mouse_clicker_start_or_restart_timer(context);
}
static void hid_mouse_clicker_exit_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
furi_timer_stop(hid_mouse_clicker->timer);
}
static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
bool consumed = false;
bool rate_changed = false;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
return false;
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
switch(event->key) {
case InputKeyOk:
model->running = !model->running;
consumed = true;
break;
case InputKeyUp:
if(model->rate < MAXIMUM_CLICK_RATE) {
model->rate++;
}
rate_changed = true;
consumed = true;
break;
case InputKeyDown:
if(model->rate > 1) {
model->rate--;
}
rate_changed = true;
consumed = true;
break;
default:
consumed = true;
break;
}
},
true);
if(rate_changed) {
hid_mouse_clicker_start_or_restart_timer(context);
}
return consumed;
}
HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {
HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker));
hid_mouse_clicker->view = view_alloc();
view_set_context(hid_mouse_clicker->view, hid_mouse_clicker);
view_allocate_model(
hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel));
view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback);
view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback);
view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback);
view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback);
hid_mouse_clicker->hid = hid;
hid_mouse_clicker->timer = furi_timer_alloc(
hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
model->transport = hid->transport;
model->rate = DEFAULT_CLICK_RATE;
},
true);
return hid_mouse_clicker;
}
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
furi_timer_stop(hid_mouse_clicker->timer);
furi_timer_free(hid_mouse_clicker->timer);
view_free(hid_mouse_clicker->view);
free(hid_mouse_clicker);
}
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
return hid_mouse_clicker->view;
}
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) {
furi_assert(hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{ model->connected = connected; },
true);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidMouseClicker HidMouseClicker;
HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid);
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker);
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker);
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected);

View File

@ -95,7 +95,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
bool consumed = false; bool consumed = false;
if(event->key == InputKeyOk) { if(event->type == InputTypeShort && event->key == InputKeyOk) {
with_view_model( with_view_model(
hid_mouse_jiggler->view, hid_mouse_jiggler->view,
HidMouseJigglerModel * model, HidMouseJigglerModel * model,

View File

@ -0,0 +1,17 @@
App(
appid="mfkey32",
name="Mfkey32",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="mfkey32_main",
requires=[
"gui",
"storage",
],
stack_size=1 * 1024,
fap_icon="mfkey.png",
fap_category="Nfc",
fap_author="noproto",
fap_icon_assets="images",
fap_weburl="https://github.com/noproto/FlipperMfkey",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
applications/external/mfkey32/mfkey.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

1349
applications/external/mfkey32/mfkey32.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) {
uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X ", csn[i]); furi_string_cat_printf(csn_str, "%02X", csn[i]);
} }
bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN);

View File

@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_auriol_th, &ws_protocol_auriol_th,
&ws_protocol_oregon_v1, &ws_protocol_oregon_v1,
&ws_protocol_tx_8300, &ws_protocol_tx_8300,
&ws_protocol_wendox_w6726,
}; };
const SubGhzProtocolRegistry weather_station_protocol_registry = { const SubGhzProtocolRegistry weather_station_protocol_registry = {

View File

@ -16,5 +16,6 @@
#include "auriol_hg0601a.h" #include "auriol_hg0601a.h"
#include "oregon_v1.h" #include "oregon_v1.h"
#include "tx_8300.h" #include "tx_8300.h"
#include "wendox_w6726.h"
extern const SubGhzProtocolRegistry weather_station_protocol_registry; extern const SubGhzProtocolRegistry weather_station_protocol_registry;

View File

@ -0,0 +1,299 @@
#include "wendox_w6726.h"
#define TAG "WSProtocolWendoxW6726"
/*
* Wendox W6726
*
* Temperature -50C to +70C
* _ _ _ __ _
* _| |___| |___| |___ ... | |_| |__...._______________
* preamble data guard time
*
* 3 reps every 3 minutes
* in the first message 11 bytes of the preamble in the rest by 7
*
* bit 0: 1955-hi, 5865-lo
* bit 1: 5865-hi, 1955-lo
* guard time: 12*1955+(lo last bit)
* data: 29 bit
*
* IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC
*
* I: identification;
* Z: temperature sign;
* T: temperature sign dependent +12C;
* B: battery low; flag to indicate low battery voltage;
* C: CRC4 (polynomial = 0x9, start_data = 0xD);
* u: unknown;
*/
static const SubGhzBlockConst ws_protocol_wendox_w6726_const = {
.te_short = 1955,
.te_long = 5865,
.te_delta = 300,
.min_count_bit_for_found = 29,
};
struct WSProtocolDecoderWendoxW6726 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
uint16_t header_count;
};
struct WSProtocolEncoderWendoxW6726 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
WendoxW6726DecoderStepReset = 0,
WendoxW6726DecoderStepCheckPreambule,
WendoxW6726DecoderStepSaveDuration,
WendoxW6726DecoderStepCheckDuration,
} WendoxW6726DecoderStep;
const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = {
.alloc = ws_protocol_decoder_wendox_w6726_alloc,
.free = ws_protocol_decoder_wendox_w6726_free,
.feed = ws_protocol_decoder_wendox_w6726_feed,
.reset = ws_protocol_decoder_wendox_w6726_reset,
.get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data,
.serialize = ws_protocol_decoder_wendox_w6726_serialize,
.deserialize = ws_protocol_decoder_wendox_w6726_deserialize,
.get_string = ws_protocol_decoder_wendox_w6726_get_string,
};
const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_wendox_w6726 = {
.name = WS_PROTOCOL_WENDOX_W6726_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_wendox_w6726_decoder,
.encoder = &ws_protocol_wendox_w6726_encoder,
};
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726));
instance->base.protocol = &ws_protocol_wendox_w6726;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_wendox_w6726_free(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
free(instance);
}
void ws_protocol_decoder_wendox_w6726_reset(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t msg[] = {
instance->decoder.decode_data >> 28,
instance->decoder.decode_data >> 20,
instance->decoder.decode_data >> 12,
instance->decoder.decode_data >> 4};
uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD);
return (crc == (instance->decoder.decode_data & 0x0F));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) {
instance->id = (instance->data >> 24) & 0xFF;
instance->battery_low = (instance->data >> 6) & 1;
instance->channel = WS_NO_CHANNEL;
if(((instance->data >> 23) & 1)) {
instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f;
} else {
instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f;
}
if(instance->temp < -50.0f) {
instance->temp = -50.0f;
} else if(instance->temp > 70.0f) {
instance->temp = 70.0f;
}
instance->btn = WS_NO_BTN;
instance->humidity = WS_NO_HUMIDITY;
}
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
switch(instance->decoder.parser_step) {
case WendoxW6726DecoderStepReset:
if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case WendoxW6726DecoderStepCheckPreambule:
if(level) {
instance->decoder.te_last = duration;
} else {
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta * 1) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2)) {
instance->header_count++;
} else if((instance->header_count > 4) && (instance->header_count < 12)) {
if((DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
}
break;
case WendoxW6726DecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
case WendoxW6726DecoderStepCheckDuration:
if(!level) {
if(duration >
ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) {
if(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
if((instance->decoder.decode_count_bit ==
ws_protocol_wendox_w6726_const.min_count_bit_for_found) &&
ws_protocol_wendox_w6726_check(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_wendox_w6726_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
}
}
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
ws_protocol_wendox_w6726_const.min_count_bit_for_found);
}
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@ -0,0 +1,80 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726"
typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726;
typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726;
extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder;
extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder;
extern const SubGhzProtocol ws_protocol_wendox_w6726;
/**
* Allocate WSProtocolDecoderWendoxW6726.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_free(void* context);
/**
* Reset decoder WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param output Resulting text
*/
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output);

View File

@ -12,7 +12,7 @@
#define MENU_ITEMS 4u #define MENU_ITEMS 4u
#define UNLOCK_CNT 3 #define UNLOCK_CNT 3
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct { typedef struct {
FuriString* item_str; FuriString* item_str;
uint8_t type; uint8_t type;
@ -69,10 +69,10 @@ void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) {
instance->view, instance->view,
WSReceiverModel * model, WSReceiverModel * model,
{ {
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0; model->u_rssi = 0;
} else { } else {
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
} }
}, },
true); true);

View File

@ -152,22 +152,22 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line,
} }
static const DuckyCmd ducky_commands[] = { static const DuckyCmd ducky_commands[] = {
{"REM ", NULL, -1}, {"REM", NULL, -1},
{"ID ", NULL, -1}, {"ID", NULL, -1},
{"DELAY ", ducky_fnc_delay, -1}, {"DELAY", ducky_fnc_delay, -1},
{"STRING ", ducky_fnc_string, 0}, {"STRING", ducky_fnc_string, 0},
{"STRINGLN ", ducky_fnc_string, 1}, {"STRINGLN", ducky_fnc_string, 1},
{"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, {"DEFAULT_DELAY", ducky_fnc_defdelay, -1},
{"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, {"DEFAULTDELAY", ducky_fnc_defdelay, -1},
{"STRINGDELAY ", ducky_fnc_strdelay, -1}, {"STRINGDELAY", ducky_fnc_strdelay, -1},
{"STRING_DELAY ", ducky_fnc_strdelay, -1}, {"STRING_DELAY", ducky_fnc_strdelay, -1},
{"REPEAT ", ducky_fnc_repeat, -1}, {"REPEAT", ducky_fnc_repeat, -1},
{"SYSRQ ", ducky_fnc_sysrq, -1}, {"SYSRQ", ducky_fnc_sysrq, -1},
{"ALTCHAR ", ducky_fnc_altchar, -1}, {"ALTCHAR", ducky_fnc_altchar, -1},
{"ALTSTRING ", ducky_fnc_altstring, -1}, {"ALTSTRING", ducky_fnc_altstring, -1},
{"ALTCODE ", ducky_fnc_altstring, -1}, {"ALTCODE", ducky_fnc_altstring, -1},
{"HOLD ", ducky_fnc_hold, -1}, {"HOLD", ducky_fnc_hold, -1},
{"RELEASE ", ducky_fnc_release, -1}, {"RELEASE", ducky_fnc_release, -1},
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
}; };
@ -175,8 +175,15 @@ static const DuckyCmd ducky_commands[] = {
#define WORKER_TAG TAG "Worker" #define WORKER_TAG TAG "Worker"
int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {
size_t cmd_word_len = strcspn(line, " ");
for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { size_t cmd_compare_len = strlen(ducky_commands[i].name);
if(cmd_compare_len != cmd_word_len) {
continue;
}
if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {
if(ducky_commands[i].callback == NULL) { if(ducky_commands[i].callback == NULL) {
return 0; return 0;
} else { } else {

View File

@ -18,7 +18,7 @@ void nfc_scene_mfkey_complete_on_enter(void* context) {
AlignCenter, AlignCenter,
AlignCenter, AlignCenter,
FontSecondary, FontSecondary,
"Now use mfkey32v2\nto extract keys"); "Now use Mfkey32\nto extract keys");
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc);
@ -46,4 +46,4 @@ void nfc_scene_mfkey_complete_on_exit(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
widget_reset(nfc->widget); widget_reset(nfc->widget);
} }

View File

@ -6,14 +6,13 @@ typedef enum {
SubGhzCustomEventManagerSetRAW, SubGhzCustomEventManagerSetRAW,
//SubmenuIndex //SubmenuIndex
SubmenuIndexPricenton, SubmenuIndexPricenton_433,
SubmenuIndexPricenton_315,
SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlo24bit,
SubmenuIndexCAME12bit, SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit, SubmenuIndexCAME24bit,
SubmenuIndexCAMETwee, SubmenuIndexCAMETwee,
SubmenuIndexNeroSketch,
SubmenuIndexNeroRadio,
SubmenuIndexGateTX, SubmenuIndexGateTX,
SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_315_00,
SubmenuIndexDoorHan_433_92, SubmenuIndexDoorHan_433_92,

View File

@ -261,7 +261,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
instance->thread = furi_thread_alloc_ex( instance->thread = furi_thread_alloc_ex(
"SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance);
SubGhz* subghz = context; SubGhz* subghz = context;
instance->setting = subghz->setting; instance->setting = subghz_txrx_get_setting(subghz->txrx);
return instance; return instance;
} }

View File

@ -0,0 +1,60 @@
#include "subghz_threshold_rssi.h"
#include <float_tools.h>
#include "../subghz_i.h"
#define TAG "SubGhzThresholdRssi"
#define THRESHOLD_RSSI_LOW_COUNT 10
struct SubGhzThresholdRssi {
float threshold_rssi;
uint8_t threshold_rssi_low_count;
};
SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void) {
SubGhzThresholdRssi* instance = malloc(sizeof(SubGhzThresholdRssi));
instance->threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;
instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;
return instance;
}
void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance) {
furi_assert(instance);
free(instance);
}
void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi) {
furi_assert(instance);
instance->threshold_rssi = rssi;
}
float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) {
furi_assert(instance);
return instance->threshold_rssi;
}
SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance) {
furi_assert(instance);
float rssi = furi_hal_subghz_get_rssi();
SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false};
if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) {
ret.is_above = true;
} else {
if(rssi < instance->threshold_rssi) {
instance->threshold_rssi_low_count++;
if(instance->threshold_rssi_low_count > THRESHOLD_RSSI_LOW_COUNT) {
instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;
}
ret.is_above = false;
} else {
instance->threshold_rssi_low_count = 0;
}
if(instance->threshold_rssi_low_count == THRESHOLD_RSSI_LOW_COUNT) {
ret.is_above = false;
} else {
ret.is_above = true;
}
}
return ret;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <furi.h>
typedef struct {
float rssi; /**< Current RSSI */
bool is_above; /**< Exceeded threshold level */
} SubGhzThresholdRssiData;
typedef struct SubGhzThresholdRssi SubGhzThresholdRssi;
/** Allocate SubGhzThresholdRssi
*
* @return SubGhzThresholdRssi*
*/
SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void);
/** Free SubGhzThresholdRssi
*
* @param instance Pointer to a SubGhzThresholdRssi
*/
void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance);
/** Set threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @param rssi RSSI threshold
*/
void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi);
/** Get threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @return float RSSI threshold
*/
float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance);
/** Check threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @return SubGhzThresholdRssiData
*/
SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance);

View File

@ -0,0 +1,521 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhz"
SubGhzTxRx* subghz_txrx_alloc() {
SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));
instance->setting = subghz_setting_alloc();
subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user"));
instance->preset = malloc(sizeof(SubGhzRadioPreset));
instance->preset->name = furi_string_alloc();
subghz_txrx_set_preset(
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
instance->txrx_state = SubGhzTxRxStateSleep;
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
instance->worker = subghz_worker_alloc();
instance->fff_data = flipper_format_string_alloc();
instance->environment = subghz_environment_alloc();
instance->is_database_loaded = subghz_environment_load_keystore(
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
subghz_environment_load_keystore(
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
subghz_environment_set_came_atomo_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
instance->environment, (void*)&subghz_protocol_registry);
instance->receiver = subghz_receiver_alloc_init(instance->environment);
subghz_worker_set_overrun_callback(
instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(instance->worker, instance->receiver);
return instance;
}
void subghz_txrx_free(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_worker_free(instance->worker);
subghz_receiver_free(instance->receiver);
subghz_environment_free(instance->environment);
flipper_format_free(instance->fff_data);
furi_string_free(instance->preset->name);
subghz_setting_free(instance->setting);
free(instance->preset);
free(instance);
}
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->is_database_loaded;
}
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size) {
furi_assert(instance);
furi_string_set(instance->preset->name, preset_name);
SubGhzRadioPreset* preset = instance->preset;
preset->frequency = frequency;
preset->data = preset_data;
preset->data_size = preset_data_size;
}
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {
UNUSED(instance);
const char* preset_name = "";
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
preset_name = "AM270";
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
preset_name = "AM650";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name = "FM238";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name = "FM476";
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
preset_name = "CUSTOM";
} else {
FURI_LOG_E(TAG, "Unknown preset");
}
return preset_name;
}
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {
furi_assert(instance);
return *instance->preset;
}
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation) {
furi_assert(instance);
SubGhzRadioPreset* preset = instance->preset;
if(frequency != NULL) {
furi_string_printf(
frequency,
"%03ld.%02ld",
preset->frequency / 1000000 % 1000,
preset->frequency / 10000 % 100);
}
if(modulation != NULL) {
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name));
}
}
static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {
furi_assert(instance);
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(preset_data);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect RX frequency.");
}
furi_assert(
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
subghz_txrx_speaker_on(instance);
furi_hal_subghz_rx();
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker);
subghz_worker_start(instance->worker);
instance->txrx_state = SubGhzTxRxStateRx;
return value;
}
static void subghz_txrx_idle(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(instance->worker)) {
subghz_worker_stop(instance->worker);
furi_hal_subghz_stop_async_rx();
}
furi_hal_subghz_idle();
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_txrx_sleep(SubGhzTxRx* instance) {
furi_assert(instance);
furi_hal_subghz_sleep();
instance->txrx_state = SubGhzTxRxStateSleep;
}
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect TX frequency.");
}
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_write(&gpio_cc1101_g0, false);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
bool ret = furi_hal_subghz_tx();
if(ret) {
subghz_txrx_speaker_on(instance);
instance->txrx_state = SubGhzTxRxStateTx;
}
return ret;
}
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
furi_assert(instance);
furi_assert(flipper_format);
subghz_txrx_stop(instance);
SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;
FuriString* temp_str = furi_string_alloc();
uint32_t repeat = 200;
do {
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubGhzTxRxStartTxStateOk;
SubGhzRadioPreset* preset = instance->preset;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
if(instance->transmitter) {
if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==
SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(preset->name), "") != 0) {
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
instance->setting, furi_string_get_cstr(preset->name)));
if(preset->frequency) {
if(!subghz_txrx_tx(instance, preset->frequency)) {
FURI_LOG_E(TAG, "Only Rx");
ret = SubGhzTxRxStartTxStateErrorOnlyRx;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
FURI_LOG_E(
TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name));
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret == SubGhzTxRxStartTxStateOk) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, instance->transmitter);
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret != SubGhzTxRxStartTxStateOk) {
subghz_transmitter_free(instance->transmitter);
if(instance->txrx_state != SubGhzTxRxStateIDLE) {
subghz_txrx_idle(instance);
}
}
} while(false);
furi_string_free(temp_str);
return ret;
}
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_txrx_stop(instance);
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));
subghz_txrx_rx(instance, instance->preset->frequency);
}
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context) {
furi_assert(instance);
instance->need_save_callback = callback;
instance->need_save_context = context;
}
static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateTx);
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_transmitter_stop(instance->transmitter);
subghz_transmitter_free(instance->transmitter);
//if protocol dynamic then we save the last upload
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
if(instance->need_save_callback) {
instance->need_save_callback(instance->need_save_context);
}
}
subghz_txrx_idle(instance);
subghz_txrx_speaker_off(instance);
//Todo: Show message
// notification_message(notifications, &sequence_reset_red);
}
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->fff_data;
}
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->setting;
}
void subghz_txrx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->txrx_state) {
case SubGhzTxRxStateTx:
subghz_txrx_tx_stop(instance);
subghz_txrx_speaker_unmute(instance);
break;
case SubGhzTxRxStateRx:
subghz_txrx_rx_end(instance);
subghz_txrx_speaker_mute(instance);
break;
default:
break;
}
}
void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->hopper_state) {
case SubGhzHopperStateOFF:
case SubGhzHopperStatePause:
return;
case SubGhzHopperStateRSSITimeOut:
if(instance->hopper_timeout != 0) {
instance->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
instance->hopper_timeout = 10;
instance->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
instance->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(instance->hopper_idx_frequency <
subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {
instance->hopper_idx_frequency++;
} else {
instance->hopper_idx_frequency = 0;
}
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
};
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
subghz_receiver_reset(instance->receiver);
instance->preset->frequency =
subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);
subghz_txrx_rx(instance, instance->preset->frequency);
}
}
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->hopper_state;
}
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {
furi_assert(instance);
instance->hopper_state = state;
}
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStatePause) {
instance->hopper_state = SubGhzHopperStateRunnig;
}
}
void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStateRunnig) {
instance->hopper_state = SubGhzHopperStatePause;
}
}
void subghz_txrx_speaker_on(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
} else {
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
furi_hal_speaker_release();
if(instance->speaker_state == SubGhzSpeakerStateShutdown)
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_mute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
}
}
}
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
}
}
}
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {
furi_assert(instance);
instance->speaker_state = state;
}
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->speaker_state;
}
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {
furi_assert(instance);
furi_assert(name_protocol);
bool res = false;
instance->decoder_result =
subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);
if(instance->decoder_result) {
res = true;
}
return res;
}
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->decoder_result;
}
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {
furi_assert(instance);
return (
(instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==
SubGhzProtocolFlag_Save);
}
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {
furi_assert(instance);
const SubGhzProtocol* protocol = instance->decoder_result->protocol;
if(check_type) {
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic);
}
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize);
}
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {
furi_assert(instance);
subghz_receiver_set_filter(instance->receiver, filter);
}
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context) {
subghz_receiver_set_rx_callback(instance->receiver, callback, context);
}
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context) {
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),
callback,
context);
}

View File

@ -0,0 +1,290 @@
#pragma once
#include "subghz_types.h"
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/raw.h>
typedef struct SubGhzTxRx SubGhzTxRx;
typedef void (*SubGhzTxRxNeedSaveCallback)(void* context);
typedef enum {
SubGhzTxRxStartTxStateOk,
SubGhzTxRxStartTxStateErrorOnlyRx,
SubGhzTxRxStartTxStateErrorParserOthers,
} SubGhzTxRxStartTxState;
/**
* Allocate SubGhzTxRx
*
* @return SubGhzTxRx* pointer to SubGhzTxRx
*/
SubGhzTxRx* subghz_txrx_alloc();
/**
* Free SubGhzTxRx
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_free(SubGhzTxRx* instance);
/**
* Check if the database is loaded
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if the database is loaded
*/
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);
/**
* Set preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param preset_data Data of preset
* @param preset_data_size Size of preset data
*/
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size);
/**
* Get name of preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset String of preset
* @return const char* Name of preset
*/
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);
/**
* Get of preset
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzRadioPreset Preset
*/
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);
/**
* Get string frequency and modulation
*
* @param instance Pointer to a SubGhzTxRx
* @param frequency Pointer to a string frequency
* @param modulation Pointer to a string modulation
*/
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation);
/**
* Start TX CC1101
*
* @param instance Pointer to a SubGhzTxRx
* @param flipper_format Pointer to a FlipperFormat
* @return SubGhzTxRxStartTxState
*/
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
/**
* Start RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_rx_start(SubGhzTxRx* instance);
/**
* Stop TX/RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_stop(SubGhzTxRx* instance);
/**
* Set sleep mode CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_sleep(SubGhzTxRx* instance);
/**
* Update frequency CC1101 in automatic mode (hopper)
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_update(SubGhzTxRx* instance);
/**
* Get state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzHopperState
*/
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);
/**
* Set state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @param state State hopper
*/
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);
/**
* Unpause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
/**
* Set pause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/**
* Speaker on
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_on(SubGhzTxRx* instance);
/**
* Speaker off
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_off(SubGhzTxRx* instance);
/**
* Speaker mute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_mute(SubGhzTxRx* instance);
/**
* Speaker unmute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance);
/**
* Set state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @param state State speaker
*/
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);
/**
* Get state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSpeakerState
*/
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);
/**
* load decoder by name protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_protocol Name protocol
* @return bool True if the decoder is loaded
*/
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);
/**
* Get decoder
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase
*/
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);
/**
* Set callback for save data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for save data
* @param context Context for callback
*/
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context);
/**
* Get pointer to a load data key
*
* @param instance Pointer to a SubGhzTxRx
* @return FlipperFormat*
*/
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);
/**
* Get pointer to a SugGhzSetting
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSetting*
*/
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);
/**
* Is it possible to save this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to save this protocol
*/
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);
/**
* Is it possible to send this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to send this protocol
*/
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);
/**
* Set filter, what types of decoder to use
*
* @param instance Pointer to a SubGhzTxRx
* @param filter Filter
*/
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);
/**
* Set callback for receive data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for receive data
* @param context Context for callback
*/
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context);
/**
* Set callback for Raw decoder, end of data transfer
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for Raw decoder, end of data transfer
* @param context Context for callback
*/
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context);

View File

@ -0,0 +1,164 @@
#include "subghz_txrx_i.h"
#include "subghz_txrx_create_protocol_key.h"
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/secplus_v1.h>
#include <lib/subghz/protocols/secplus_v2.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/raw.h>
#define TAG "SubGhzCreateProtocolKey"
bool subghz_txrx_gen_data_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit) {
furi_assert(context);
SubGhzTxRx* instance = context;
bool res = false;
subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0);
instance->decoder_result =
subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name);
if(instance->decoder_result == NULL) {
//TODO: Error
// furi_string_set(error_str, "Protocol not\nfound!");
// scene_manager_next_scene(scene_manager, SubGhzSceneShowErrorSub);
FURI_LOG_E(TAG, "Protocol not found!");
return false;
}
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data);
stream_clean(fff_data_stream);
if(subghz_protocol_decoder_base_serialize(
instance->decoder_result, instance->fff_data, instance->preset) !=
SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
if(!flipper_format_update_uint32(instance->fff_data, "Bit", &bit, 1)) {
FURI_LOG_E(TAG, "Unable to update Bit");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(instance->fff_data, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Key");
break;
}
res = true;
} while(false);
return res;
}
bool subghz_txrx_gen_data_protocol_and_te(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t te) {
furi_assert(instance);
bool ret = false;
if(subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit)) {
if(!flipper_format_update_uint32(instance->fff_data, "TE", (uint32_t*)&te, 1)) {
FURI_LOG_E(TAG, "Unable to update Te");
} else {
ret = true;
}
}
return ret;
}
bool subghz_txrx_gen_keeloq_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
const char* name_sysmem,
uint32_t serial,
uint8_t btn,
uint16_t cnt) {
furi_assert(instance);
bool ret = false;
serial &= 0x0FFFFFFF;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);
if(instance->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(instance->transmitter),
instance->fff_data,
serial,
btn,
cnt,
name_sysmem,
instance->preset);
ret = true;
}
subghz_transmitter_free(instance->transmitter);
return ret;
}
bool subghz_txrx_gen_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint32_t cnt) {
furi_assert(instance);
bool ret = false;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);
if(instance->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(instance->transmitter),
instance->fff_data,
serial,
btn,
cnt,
instance->preset);
ret = true;
}
return ret;
}
bool subghz_txrx_gen_secplus_v1_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency) {
furi_assert(instance);
bool ret = false;
uint32_t serial = (uint32_t)rand();
while(!subghz_protocol_secplus_v1_check_fixed(serial)) {
serial = (uint32_t)rand();
}
if(subghz_txrx_gen_data_protocol(
instance,
name_preset,
frequency,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)serial << 32 | 0xE6000000,
42)) {
ret = true;
}
return ret;
}

View File

@ -0,0 +1,96 @@
#pragma once
#include "subghz_types.h"
#include "subghz_txrx.h"
/**
* Generate data for protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param protocol_name Name of protocol
* @param key Key
* @param bit Bit
* @return bool True if success
*/
bool subghz_txrx_gen_data_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit);
/**
* Generate data for protocol and te
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param protocol_name Name of protocol
* @param key Key
* @param bit Bit
* @param te Te
* @return bool True if success
*/
bool subghz_txrx_gen_data_protocol_and_te(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t te);
/**
* Generate data Keeloq protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @param name_sysmem Name of Keeloq sysmem
* @param serial Serial number
* @param btn Button
* @param cnt Counter
* @return bool True if success
*/
bool subghz_txrx_gen_keeloq_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
const char* name_sysmem,
uint32_t serial,
uint8_t btn,
uint16_t cnt);
/**
* Generate data SecPlus v2 protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @param serial Serial number
* @param btn Button
* @param cnt Counter
* @return bool True if success
*/
bool subghz_txrx_gen_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint32_t cnt);
/**
* Generate data SecPlus v1 protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @return bool True if success
*/
bool subghz_txrx_gen_secplus_v1_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency);

View File

@ -0,0 +1,27 @@
#pragma once
#include "subghz_txrx.h"
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SubGhzRadioPreset* preset;
SubGhzSetting* setting;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
bool is_database_loaded;
SubGhzHopperState hopper_state;
SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state;
SubGhzTxRxNeedSaveCallback need_save_callback;
void* need_save_context;
};

View File

@ -77,3 +77,10 @@ typedef enum {
SubGhzViewIdTestCarrier, SubGhzViewIdTestCarrier,
SubGhzViewIdTestPacket, SubGhzViewIdTestPacket,
} SubGhzViewId; } SubGhzViewId;
/** SubGhz load type file */
typedef enum {
SubGhzLoadTypeFileNoLoad,
SubGhzLoadTypeFileKey,
SubGhzLoadTypeFileRaw,
} SubGhzLoadTypeFile;

View File

@ -19,7 +19,7 @@ void subghz_scene_delete_on_enter(void* context) {
modulation_str = furi_string_alloc(); modulation_str = furi_string_alloc();
text = furi_string_alloc(); text = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
78, 78,
@ -37,7 +37,7 @@ void subghz_scene_delete_on_enter(void* context) {
AlignTop, AlignTop,
FontSecondary, FontSecondary,
furi_string_get_cstr(modulation_str)); furi_string_get_cstr(modulation_str));
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text);
widget_add_string_multiline_element( widget_add_string_multiline_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text));

View File

@ -33,7 +33,7 @@ void subghz_scene_delete_raw_on_enter(void* context) {
widget_add_string_element( widget_add_string_element(
subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal");
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
35, 35,

View File

@ -37,27 +37,23 @@ void subghz_scene_need_saving_on_enter(void* context) {
bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeBack) { if(event.type == SceneManagerEventTypeBack) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
return true; return true;
} else if(event.type == SceneManagerEventTypeCustom) { } else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneStay) { if(event.event == SubGhzCustomEventSceneStay) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
return true; return true;
} else if(event.event == SubGhzCustomEventSceneExit) { } else if(event.event == SubGhzCustomEventSceneExit) {
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { SubGhzRxKeyState state = subghz_rx_key_state_get(subghz);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz_preset_init(
subghz, if(state == SubGhzRxKeyStateExit) {
"AM650", subghz_set_default_preset(subghz);
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart); subghz->scene_manager, SubGhzSceneStart);
} else { } else {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
} }

View File

@ -3,11 +3,9 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <lib/subghz/protocols/raw.h> #include <lib/subghz/protocols/raw.h>
#include <lib/toolbox/path.h> #include <lib/toolbox/path.h>
#include <float_tools.h>
#define RAW_FILE_NAME "Raw_signal_" #define RAW_FILE_NAME "Raw_signal_"
#define TAG "SubGhzSceneReadRAW" #define TAG "SubGhzSceneReadRAW"
#define RAW_THRESHOLD_RSSI_LOW_COUNT 10
bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
bool ret = false; bool ret = false;
@ -15,12 +13,13 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
FuriString* temp_str; FuriString* temp_str;
temp_str = furi_string_alloc(); temp_str = furi_string_alloc();
do { do {
if(!flipper_format_rewind(subghz->txrx->fff_data)) { FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
if(!flipper_format_rewind(fff_data)) {
FURI_LOG_E(TAG, "Rewind error"); FURI_LOG_E(TAG, "Rewind error");
break; break;
} }
if(!flipper_format_read_string(subghz->txrx->fff_data, "File_name", temp_str)) { if(!flipper_format_read_string(fff_data, "File_name", temp_str)) {
FURI_LOG_E(TAG, "Missing File_name"); FURI_LOG_E(TAG, "Missing File_name");
break; break;
} }
@ -38,13 +37,10 @@ static void subghz_scene_read_raw_update_statusbar(void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* frequency_str; FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str; FuriString* modulation_str = furi_string_alloc();
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_read_raw_add_data_statusbar( subghz_read_raw_add_data_statusbar(
subghz->subghz_read_raw, subghz->subghz_read_raw,
furi_string_get_cstr(frequency_str), furi_string_get_cstr(frequency_str),
@ -69,13 +65,13 @@ void subghz_scene_read_raw_callback_end_tx(void* context) {
void subghz_scene_read_raw_on_enter(void* context) { void subghz_scene_read_raw_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* file_name; FuriString* file_name = furi_string_alloc();
file_name = furi_string_alloc();
switch(subghz->txrx->rx_key_state) { float threshold_rssi = subghz_threshold_rssi_get(subghz->threshold_rssi);
switch(subghz_rx_key_state_get(subghz)) {
case SubGhzRxKeyStateBack: case SubGhzRxKeyStateBack:
subghz_read_raw_set_status( subghz_read_raw_set_status(
subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi);
break; break;
case SubGhzRxKeyStateRAWLoad: case SubGhzRxKeyStateRAWLoad:
path_extract_filename(subghz->file_path, file_name, true); path_extract_filename(subghz->file_path, file_name, true);
@ -83,8 +79,7 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->subghz_read_raw, subghz->subghz_read_raw,
SubGhzReadRAWStatusLoadKeyTX, SubGhzReadRAWStatusLoadKeyTX,
furi_string_get_cstr(file_name), furi_string_get_cstr(file_name),
subghz->txrx->raw_threshold_rssi); threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
case SubGhzRxKeyStateRAWSave: case SubGhzRxKeyStateRAWSave:
path_extract_filename(subghz->file_path, file_name, true); path_extract_filename(subghz->file_path, file_name, true);
@ -92,66 +87,51 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->subghz_read_raw, subghz->subghz_read_raw,
SubGhzReadRAWStatusSaveKey, SubGhzReadRAWStatusSaveKey,
furi_string_get_cstr(file_name), furi_string_get_cstr(file_name),
subghz->txrx->raw_threshold_rssi); threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
default: default:
subghz_read_raw_set_status( subghz_read_raw_set_status(
subghz->subghz_read_raw, subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "", threshold_rssi);
SubGhzReadRAWStatusStart,
"",
subghz->txrx->raw_threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
} }
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
}
furi_string_free(file_name); furi_string_free(file_name);
subghz_scene_read_raw_update_statusbar(subghz); subghz_scene_read_raw_update_statusbar(subghz);
//set callback view raw //set callback view raw
subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz);
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME));
subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME);
furi_assert(subghz->txrx->decoder_result);
//set filter RAW feed //set filter RAW feed
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);
} }
bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
bool consumed = false; bool consumed = false;
SubGhzProtocolDecoderRAW* decoder_raw =
(SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx);
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case SubGhzCustomEventViewReadRAWBack: case SubGhzCustomEventViewReadRAWBack:
//Stop TX
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
//Stop RX
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
//Stop save file //Stop save file
subghz_protocol_raw_save_to_file_stop( subghz_protocol_raw_save_to_file_stop(decoder_raw);
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
//needed save? //needed save?
if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) ||
(subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) { (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
//Restore default setting //Restore default setting
subghz_preset_init( subghz_set_default_preset(subghz);
subghz,
"AM650",
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) { subghz->scene_manager, SubGhzSceneSaved)) {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@ -165,16 +145,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break; break;
case SubGhzCustomEventViewReadRAWTXRXStop: case SubGhzCustomEventViewReadRAWTXRXStop:
//Stop TX subghz_txrx_stop(subghz->txrx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
//Stop RX
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
consumed = true; consumed = true;
break; break;
@ -187,13 +158,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break; break;
case SubGhzCustomEventViewReadRAWErase: case SubGhzCustomEventViewReadRAWErase:
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
if(subghz_scene_read_raw_update_filename(subghz)) { if(subghz_scene_read_raw_update_filename(subghz)) {
furi_string_set(subghz->file_path_tmp, subghz->file_path); furi_string_set(subghz->file_path_tmp, subghz->file_path);
subghz_delete_file(subghz); subghz_delete_file(subghz);
} }
} }
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
notification_message(subghz->notifications, &sequence_reset_rgb); notification_message(subghz->notifications, &sequence_reset_rgb);
consumed = true; consumed = true;
break; break;
@ -203,7 +174,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_scene_read_raw_update_filename(subghz)) { if(subghz_scene_read_raw_update_filename(subghz)) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
consumed = true; consumed = true;
} else { } else {
@ -223,33 +194,22 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {
//start send //start send
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
subghz_rx_end(subghz); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
} subghz_read_raw_set_status(
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz->subghz_read_raw,
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { SubGhzReadRAWStatusIDLE,
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { "",
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_threshold_rssi_get(subghz->threshold_rssi));
subghz_read_raw_set_status( } else {
subghz->subghz_read_raw, if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) ||
SubGhzReadRAWStatusIDLE, !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) {
"", DOLPHIN_DEED(DolphinDeedSubGhzSend);
subghz->txrx->raw_threshold_rssi);
} else {
if(scene_manager_has_previous_scene(
subghz->scene_manager, SubGhzSceneSaved) ||
!scene_manager_has_previous_scene(
subghz->scene_manager, SubGhzSceneStart)) {
DOLPHIN_DEED(DolphinDeedSubGhzSend);
}
// set callback end tx
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(
subghz->txrx->transmitter),
subghz_scene_read_raw_callback_end_tx,
subghz);
subghz->state_notifications = SubGhzNotificationStateTx;
} }
// set callback end tx
subghz_txrx_set_raw_file_encoder_worker_callback_end(
subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz);
subghz->state_notifications = SubGhzNotificationStateTx;
} }
} else { } else {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@ -263,33 +223,22 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReadRAWSendStop: case SubGhzCustomEventViewReadRAWSendStop:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_speaker_unmute(subghz);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
subghz_read_raw_stop_send(subghz->subghz_read_raw); subghz_read_raw_stop_send(subghz->subghz_read_raw);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReadRAWIDLE: case SubGhzCustomEventViewReadRAWIDLE:
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz); size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw);
subghz_sleep(subghz);
};
size_t spl_count = subghz_protocol_raw_get_sample_write( subghz_protocol_raw_save_to_file_stop(decoder_raw);
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
subghz_protocol_raw_save_to_file_stop( FuriString* temp_str = furi_string_alloc();
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_printf( furi_string_printf(
temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION);
subghz_protocol_raw_gen_fff_data( subghz_protocol_raw_gen_fff_data(
subghz->txrx->fff_data, furi_string_get_cstr(temp_str)); subghz_txrx_get_fff_data(subghz->txrx), furi_string_get_cstr(temp_str));
furi_string_free(temp_str); furi_string_free(temp_str);
if(spl_count > 0) { if(spl_count > 0) {
@ -299,32 +248,21 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
} }
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReadRAWREC: case SubGhzCustomEventViewReadRAWREC:
if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateIDLE) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz_protocol_raw_save_to_file_init( if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) {
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result,
RAW_FILE_NAME,
subghz->txrx->preset)) {
DOLPHIN_DEED(DolphinDeedSubGhzRawRec); DOLPHIN_DEED(DolphinDeedSubGhzRawRec);
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz_txrx_rx_start(subghz->txrx);
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting,
furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} else { } else {
furi_string_set(subghz->error_str, "Function requires\nan SD card."); furi_string_set(subghz->error_str, "Function requires\nan SD card.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
@ -337,7 +275,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW);
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
} else { } else {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@ -356,41 +294,15 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateRx: case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10); notification_message(subghz->notifications, &sequence_blink_cyan_10);
subghz_read_raw_update_sample_write( subghz_read_raw_update_sample_write(
subghz->subghz_read_raw, subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw));
subghz_protocol_raw_get_sample_write(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result));
float rssi = furi_hal_subghz_get_rssi();
if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
} else {
if(rssi < subghz->txrx->raw_threshold_rssi) {
subghz->txrx->raw_threshold_rssi_low_count++;
if(subghz->txrx->raw_threshold_rssi_low_count > RAW_THRESHOLD_RSSI_LOW_COUNT) {
subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT;
}
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
} else {
subghz->txrx->raw_threshold_rssi_low_count = 0;
}
if(subghz->txrx->raw_threshold_rssi_low_count == RAW_THRESHOLD_RSSI_LOW_COUNT) {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true);
subghz_speaker_mute(subghz);
} else {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
subghz_speaker_unmute(subghz);
}
}
SubGhzThresholdRssiData ret_rssi =
subghz_threshold_get_rssi_data(subghz->threshold_rssi);
subghz_read_raw_add_data_rssi(
subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above);
subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above);
break; break;
case SubGhzNotificationStateTx: case SubGhzNotificationStateTx:
notification_message(subghz->notifications, &sequence_blink_magenta_10); notification_message(subghz->notifications, &sequence_blink_magenta_10);
@ -407,13 +319,10 @@ void subghz_scene_read_raw_on_exit(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
//Stop CC1101 //Stop CC1101
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
notification_message(subghz->notifications, &sequence_reset_rgb); notification_message(subghz->notifications, &sequence_reset_rgb);
//filter restoration //filter restoration
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
} }

View File

@ -35,16 +35,12 @@ static const NotificationSequence subghs_sequence_rx_locked = {
static void subghz_scene_receiver_update_statusbar(void* context) { static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* history_stat_str; FuriString* history_stat_str = furi_string_alloc();
history_stat_str = furi_string_alloc(); if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) {
if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { FuriString* frequency_str = furi_string_alloc();
FuriString* frequency_str; FuriString* modulation_str = furi_string_alloc();
FuriString* modulation_str;
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_view_receiver_add_data_statusbar( subghz_view_receiver_add_data_statusbar(
subghz->subghz_receiver, subghz->subghz_receiver,
@ -74,80 +70,68 @@ static void subghz_scene_add_to_history_callback(
void* context) { void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* str_buff; SubGhzHistory* history = subghz->history;
str_buff = furi_string_alloc(); FuriString* str_buff = furi_string_alloc();
if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz_history_add_to_history(history, decoder_base, &preset)) {
furi_string_reset(str_buff); furi_string_reset(str_buff);
subghz->state_notifications = SubGhzNotificationStateRxDone; subghz->state_notifications = SubGhzNotificationStateRxDone;
uint16_t item_history = subghz_history_get_item(history);
subghz_history_get_text_item_menu( subghz_history_get_text_item_menu(history, str_buff, item_history - 1);
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
subghz_view_receiver_add_item_to_menu( subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver, subghz->subghz_receiver,
furi_string_get_cstr(str_buff), furi_string_get_cstr(str_buff),
subghz_history_get_type_protocol( subghz_history_get_type_protocol(history, item_history - 1));
subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1));
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
subghz_receiver_reset(receiver); subghz_receiver_reset(receiver);
furi_string_free(str_buff); furi_string_free(str_buff);
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} }
void subghz_scene_receiver_on_enter(void* context) { void subghz_scene_receiver_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzHistory* history = subghz->history;
FuriString* str_buff; FuriString* str_buff;
str_buff = furi_string_alloc(); str_buff = furi_string_alloc();
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) {
subghz_preset_init( subghz_set_default_preset(subghz);
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); subghz_history_reset(history);
subghz_history_reset(subghz->txrx->history); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart);
subghz->txrx->rx_key_state = SubGhzRxKeyStateStart;
} }
subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));
//Load history to receiver //Load history to receiver
subghz_view_receiver_exit(subghz->subghz_receiver); subghz_view_receiver_exit(subghz->subghz_receiver);
for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { for(uint8_t i = 0; i < subghz_history_get_item(history); i++) {
furi_string_reset(str_buff); furi_string_reset(str_buff);
subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); subghz_history_get_text_item_menu(history, str_buff, i);
subghz_view_receiver_add_item_to_menu( subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver, subghz->subghz_receiver,
furi_string_get_cstr(str_buff), furi_string_get_cstr(str_buff),
subghz_history_get_type_protocol(subghz->txrx->history, i)); subghz_history_get_type_protocol(history, i));
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} }
furi_string_free(str_buff); furi_string_free(str_buff);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
subghz_view_receiver_set_callback( subghz_view_receiver_set_callback(
subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);
subghz_receiver_set_rx_callback( subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_rx_start(subghz->txrx);
subghz_rx_end(subghz); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
};
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
//to use a universal decoder, we are looking for a link to it //to use a universal decoder, we are looking for a link to it
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( furi_check(
subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME));
furi_assert(subghz->txrx->decoder_result);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
} }
@ -160,41 +144,31 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReceiverBack: case SubGhzCustomEventViewReceiverBack:
// Stop CC1101 Rx // Stop CC1101 Rx
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
subghz_sleep(subghz); subghz->idx_menu_chosen = 0;
}; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->idx_menu_chosen = 0;
subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz);
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz_preset_init( subghz_set_default_preset(subghz);
subghz,
"AM650",
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart); subghz->scene_manager, SubGhzSceneStart);
} }
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverOK: case SubGhzCustomEventViewReceiverOK:
subghz->txrx->idx_menu_chosen = subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverConfig: case SubGhzCustomEventViewReceiverConfig:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->idx_menu_chosen = subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
consumed = true; consumed = true;
break; break;
@ -203,30 +177,30 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverUnlock: case SubGhzCustomEventViewReceiverUnlock:
subghz->lock = SubGhzLockOff; subghz_unlock(subghz);
consumed = true; consumed = true;
break; break;
default: default:
break; break;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz); subghz_txrx_hopper_update(subghz->txrx);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
//get RSSI SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi);
float rssi = furi_hal_subghz_get_rssi();
subghz_receiver_rssi(subghz->subghz_receiver, rssi); subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi);
subghz_protocol_decoder_bin_raw_data_input_rssi( subghz_protocol_decoder_bin_raw_data_input_rssi(
(SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi);
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateRx: case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10); notification_message(subghz->notifications, &sequence_blink_cyan_10);
break; break;
case SubGhzNotificationStateRxDone: case SubGhzNotificationStateRxDone:
if(subghz->lock != SubGhzLockOn) { if(!subghz_is_locked(subghz)) {
notification_message(subghz->notifications, &subghs_sequence_rx); notification_message(subghz->notifications, &subghs_sequence_rx);
} else { } else {
notification_message(subghz->notifications, &subghs_sequence_rx_locked); notification_message(subghz->notifications, &subghs_sequence_rx_locked);

View File

@ -72,13 +72,15 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) {
if(value == subghz_setting_get_frequency(subghz->setting, i)) { if(value == subghz_setting_get_frequency(setting, i)) {
index = i; index = i;
break; break;
} else { } else {
index = subghz_setting_get_frequency_default_index(subghz->setting); index = subghz_setting_get_frequency_default_index(setting);
} }
} }
return index; return index;
@ -87,13 +89,15 @@ uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void*
uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) {
if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) {
index = i; index = i;
break; break;
} else { } else {
// index = subghz_setting_get_frequency_default_index(subghz->setting); // index = subghz_setting_get_frequency_default_index(subghz_txrx_get_setting(subghz->txrx));
} }
} }
return index; return index;
@ -122,70 +126,84 @@ uint8_t subghz_scene_receiver_config_hopper_value_index(
static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) {
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_frequency(setting, index);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_frequency(subghz->setting, index) / 1000000, frequency / 1000000,
(subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf); variable_item_set_current_value_text(item, text_buf);
subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); subghz_txrx_set_preset(
subghz->txrx,
furi_string_get_cstr(preset.name),
frequency,
preset.data,
preset.data_size);
} else { } else {
variable_item_set_current_value_index( variable_item_set_current_value_index(
item, subghz_setting_get_frequency_default_index(subghz->setting)); item, subghz_setting_get_frequency_default_index(setting));
} }
} }
static void subghz_scene_receiver_config_set_preset(VariableItem* item) { static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text( SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
item, subghz_setting_get_preset_name(subghz->setting, index));
subghz_preset_init( variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index));
subghz,
subghz_setting_get_preset_name(subghz->setting, index), SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
subghz->txrx->preset->frequency, subghz_txrx_set_preset(
subghz_setting_get_preset_data(subghz->setting, index), subghz->txrx,
subghz_setting_get_preset_data_size(subghz->setting, index)); subghz_setting_get_preset_name(setting, index),
preset.frequency,
subghz_setting_get_preset_data(setting, index),
subghz_setting_get_preset_data_size(setting, index));
} }
static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig);
variable_item_set_current_value_text(item, hopping_text[index]); variable_item_set_current_value_text(item, hopping_text[index]);
if(hopping_value[index] == SubGhzHopperStateOFF) { if(hopping_value[index] == SubGhzHopperStateOFF) {
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_default_frequency(setting);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_default_frequency(subghz->setting) / 1000000, frequency / 1000000,
(subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text( variable_item_set_current_value_text(frequency_item, text_buf);
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig), subghz_txrx_set_preset(
text_buf); subghz->txrx,
subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); furi_string_get_cstr(preset.name),
frequency,
preset.data,
preset.data_size);
variable_item_set_current_value_index( variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state( frequency_item, subghz_setting_get_frequency_default_index(setting));
subghz->scene_manager, SubGhzSceneReceiverConfig),
subghz_setting_get_frequency_default_index(subghz->setting));
} else { } else {
variable_item_set_current_value_text( variable_item_set_current_value_text(frequency_item, " -----");
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig),
" -----");
variable_item_set_current_value_index( variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state( frequency_item, subghz_setting_get_frequency_default_index(setting));
subghz->scene_manager, SubGhzSceneReceiverConfig),
subghz_setting_get_frequency_default_index(subghz->setting));
} }
subghz->txrx->hopper_state = hopping_value[index]; subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]);
} }
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@ -193,7 +211,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, speaker_text[index]); variable_item_set_current_value_text(item, speaker_text[index]);
subghz->txrx->speaker_state = speaker_value[index]; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]);
} }
static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
@ -201,8 +219,8 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, bin_raw_text[index]); variable_item_set_current_value_text(item, bin_raw_text[index]);
subghz->txrx->filter = bin_raw_value[index]; subghz->filter = bin_raw_value[index];
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
} }
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
@ -210,7 +228,7 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]);
subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; subghz_threshold_rssi_set(subghz->threshold_rssi, raw_theshold_rssi_value[index]);
} }
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
@ -226,25 +244,27 @@ void subghz_scene_receiver_config_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
VariableItem* item; VariableItem* item;
uint8_t value_index; uint8_t value_index;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
item = variable_item_list_add( item = variable_item_list_add(
subghz->variable_item_list, subghz->variable_item_list,
"Frequency:", "Frequency:",
subghz_setting_get_frequency_count(subghz->setting), subghz_setting_get_frequency_count(setting),
subghz_scene_receiver_config_set_frequency, subghz_scene_receiver_config_set_frequency,
subghz); subghz);
value_index = value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz);
subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz);
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_frequency(setting, value_index);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, frequency / 1000000,
(subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf); variable_item_set_current_value_text(item, text_buf);
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
@ -256,7 +276,7 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_set_hopping_running, subghz_scene_receiver_config_set_hopping_running,
subghz); subghz);
value_index = subghz_scene_receiver_config_hopper_value_index( value_index = subghz_scene_receiver_config_hopper_value_index(
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_text[value_index]); variable_item_set_current_value_text(item, hopping_text[value_index]);
} }
@ -264,14 +284,14 @@ void subghz_scene_receiver_config_on_enter(void* context) {
item = variable_item_list_add( item = variable_item_list_add(
subghz->variable_item_list, subghz->variable_item_list,
"Modulation:", "Modulation:",
subghz_setting_get_preset_count(subghz->setting), subghz_setting_get_preset_count(setting),
subghz_scene_receiver_config_set_preset, subghz_scene_receiver_config_set_preset,
subghz); subghz);
value_index = subghz_scene_receiver_config_next_preset( value_index =
furi_string_get_cstr(subghz->txrx->preset->name), subghz); subghz_scene_receiver_config_next_preset(furi_string_get_cstr(preset.name), subghz);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text( variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(subghz->setting, value_index)); item, subghz_setting_get_preset_name(setting, value_index));
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerSet) { SubGhzCustomEventManagerSet) {
@ -281,7 +301,7 @@ void subghz_scene_receiver_config_on_enter(void* context) {
BIN_RAW_COUNT, BIN_RAW_COUNT,
subghz_scene_receiver_config_set_bin_raw, subghz_scene_receiver_config_set_bin_raw,
subghz); subghz);
value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, bin_raw_text[value_index]); variable_item_set_current_value_text(item, bin_raw_text[value_index]);
} }
@ -292,7 +312,8 @@ void subghz_scene_receiver_config_on_enter(void* context) {
SPEAKER_COUNT, SPEAKER_COUNT,
subghz_scene_receiver_config_set_speaker, subghz_scene_receiver_config_set_speaker,
subghz); subghz);
value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); value_index = value_index_uint32(
subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, speaker_text[value_index]); variable_item_set_current_value_text(item, speaker_text[value_index]);
@ -313,7 +334,9 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_set_raw_threshold_rssi, subghz_scene_receiver_config_set_raw_threshold_rssi,
subghz); subghz);
value_index = value_index_float( value_index = value_index_float(
subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); subghz_threshold_rssi_get(subghz->threshold_rssi),
raw_theshold_rssi_value,
RAW_THRESHOLD_RSSI_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);
} }
@ -326,7 +349,7 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneSettingLock) { if(event.event == SubGhzCustomEventSceneSettingLock) {
subghz->lock = SubGhzLockOn; subghz_lock(subghz);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
consumed = true; consumed = true;
} }

View File

@ -19,20 +19,19 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
static bool subghz_scene_receiver_info_update_parser(void* context) { static bool subghz_scene_receiver_info_update_parser(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver,
subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
if(subghz->txrx->decoder_result) { if(subghz_txrx_load_decoder_by_name_protocol(
subghz->txrx,
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
//todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
subghz_protocol_decoder_base_deserialize( subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz_txrx_get_decoder(subghz->txrx),
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
SubGhzRadioPreset* preset = SubGhzRadioPreset* preset =
subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);
subghz_preset_init( subghz_txrx_set_preset(
subghz, subghz->txrx,
furi_string_get_cstr(preset->name), furi_string_get_cstr(preset->name),
preset->frequency, preset->frequency,
preset->data, preset->data,
@ -47,15 +46,11 @@ void subghz_scene_receiver_info_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(subghz_scene_receiver_info_update_parser(subghz)) { if(subghz_scene_receiver_info_update_parser(subghz)) {
FuriString* frequency_str; FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str; FuriString* modulation_str = furi_string_alloc();
FuriString* text; FuriString* text = furi_string_alloc();
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
text = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
78, 78,
@ -73,7 +68,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
AlignTop, AlignTop,
FontSecondary, FontSecondary,
furi_string_get_cstr(modulation_str)); furi_string_get_cstr(modulation_str));
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text);
widget_add_string_multiline_element( widget_add_string_multiline_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text));
@ -81,8 +76,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
furi_string_free(modulation_str); furi_string_free(modulation_str);
furi_string_free(text); furi_string_free(text);
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
SubGhzProtocolFlag_Save) {
widget_add_button_element( widget_add_button_element(
subghz->widget, subghz->widget,
GuiButtonTypeRight, GuiButtonTypeRight,
@ -90,10 +84,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
subghz_scene_receiver_info_callback, subghz_scene_receiver_info_callback,
subghz); subghz);
} }
if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) {
SubGhzProtocolFlag_Send) &&
subghz->txrx->decoder_result->protocol->encoder->deserialize &&
subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) {
widget_add_button_element( widget_add_button_element(
subghz->widget, subghz->widget,
GuiButtonTypeCenter, GuiButtonTypeCenter,
@ -114,82 +105,49 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
SubGhz* subghz = context; SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) {
//CC1101 Stop RX -> Start TX
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz->txrx->hopper_state = SubGhzHopperStatePause;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
}
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || //CC1101 Stop RX -> Start TX
subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { subghz_txrx_hopper_pause(subghz->txrx);
if(!subghz_tx_start( if(!subghz_tx_start(
subghz, subghz,
subghz_history_get_raw_data( subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { subghz_txrx_rx_start(subghz->txrx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_hopper_unpause(subghz->txrx);
subghz_tx_stop(subghz); subghz->state_notifications = SubGhzNotificationStateRx;
} } else {
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz->state_notifications = SubGhzNotificationStateTx;
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting,
furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
subghz->state_notifications = SubGhzNotificationStateRx;
} else {
subghz->state_notifications = SubGhzNotificationStateTx;
}
} }
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
//CC1101 Stop Tx -> Start RX //CC1101 Stop Tx -> Start RX
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz); subghz_txrx_rx_start(subghz->txrx);
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_txrx_hopper_unpause(subghz->txrx);
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
//CC1101 Stop RX -> Save //CC1101 Stop RX -> Save
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz); subghz_txrx_stop(subghz->txrx);
subghz_sleep(subghz);
}
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
SubGhzProtocolFlag_Save) {
subghz_file_name_clear(subghz); subghz_file_name_clear(subghz);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
} }
return true; return true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz); subghz_txrx_hopper_update(subghz->txrx);
} }
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateTx: case SubGhzNotificationStateTx:

View File

@ -5,8 +5,7 @@
void subghz_scene_region_info_on_enter(void* context) { void subghz_scene_region_info_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
const FuriHalRegion* const region = furi_hal_region_get(); const FuriHalRegion* const region = furi_hal_region_get();
FuriString* buffer; FuriString* buffer = furi_string_alloc();
buffer = furi_string_alloc();
if(region) { if(region) {
furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code);
for(uint16_t i = 0; i < region->bands_count; ++i) { for(uint16_t i = 0; i < region->bands_count; ++i) {

View File

@ -3,6 +3,7 @@
typedef enum { typedef enum {
SubGhzRpcStateIdle, SubGhzRpcStateIdle,
SubGhzRpcStateLoaded, SubGhzRpcStateLoaded,
SubGhzRpcStateTx,
} SubGhzRpcState; } SubGhzRpcState;
void subghz_scene_rpc_on_enter(void* context) { void subghz_scene_rpc_on_enter(void* context) {
@ -38,9 +39,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_stop(subghz->view_dispatcher); view_dispatcher_stop(subghz->view_dispatcher);
} else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {
bool result = false; bool result = false;
if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && if((state == SubGhzRpcStateLoaded)) {
(state == SubGhzRpcStateLoaded)) { result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
result = subghz_tx_start(subghz, subghz->txrx->fff_data); state = SubGhzRpcStateTx;
if(result) subghz_blink_start(subghz); if(result) subghz_blink_start(subghz);
} }
if(!result) { if(!result) {
@ -52,10 +53,10 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
bool result = false; bool result = false;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { if(state == SubGhzRpcStateTx) {
subghz_txrx_stop(subghz->txrx);
subghz_blink_stop(subghz); subghz_blink_stop(subghz);
subghz_tx_stop(subghz); state = SubGhzRpcStateIdle;
subghz_sleep(subghz);
result = true; result = true;
} }
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result);
@ -93,10 +94,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
void subghz_scene_rpc_on_exit(void* context) { void subghz_scene_rpc_on_exit(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { if(state != SubGhzRpcStateIdle) {
subghz_tx_stop(subghz); subghz_txrx_stop(subghz->txrx);
subghz_sleep(subghz);
subghz_blink_stop(subghz); subghz_blink_stop(subghz);
} }

View File

@ -35,10 +35,8 @@ void subghz_scene_save_name_on_enter(void* context) {
TextInput* text_input = subghz->text_input; TextInput* text_input = subghz->text_input;
bool dev_name_empty = false; bool dev_name_empty = false;
FuriString* file_name; FuriString* file_name = furi_string_alloc();
FuriString* dir_name; FuriString* dir_name = furi_string_alloc();
file_name = furi_string_alloc();
dir_name = furi_string_alloc();
if(!subghz_path_is_file(subghz->file_path)) { if(!subghz_path_is_file(subghz->file_path)) {
char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0};
@ -69,7 +67,7 @@ void subghz_scene_save_name_on_enter(void* context) {
subghz_scene_save_name_text_input_callback, subghz_scene_save_name_text_input_callback,
subghz, subghz,
subghz->file_name_tmp, subghz->file_name_tmp,
MAX_TEXT_INPUT_LEN, // buffer size MAX_TEXT_INPUT_LEN,
dev_name_empty); dev_name_empty);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
@ -106,7 +104,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
SubGhzCustomEventManagerNoSet) { SubGhzCustomEventManagerNoSet) {
subghz_save_protocol_to_file( subghz_save_protocol_to_file(
subghz, subghz,
subghz->txrx->fff_data, subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path)); furi_string_get_cstr(subghz->file_path));
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, subghz->scene_manager,
@ -115,8 +113,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else { } else {
subghz_save_protocol_to_file( subghz_save_protocol_to_file(
subghz, subghz,
subghz_history_get_raw_data( subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen),
subghz->txrx->history, subghz->txrx->idx_menu_chosen),
furi_string_get_cstr(subghz->file_path)); furi_string_get_cstr(subghz->file_path));
} }
} }
@ -124,7 +121,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerNoSet) { SubGhzCustomEventManagerNoSet) {
subghz_protocol_raw_gen_fff_data( subghz_protocol_raw_gen_fff_data(
subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path));
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
} else { } else {

View File

@ -26,10 +26,10 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
if(event.event == SubGhzCustomEventSceneSaveSuccess) { if(event.event == SubGhzCustomEventSceneSaveSuccess) {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneReceiver)) { subghz->scene_manager, SubGhzSceneReceiver)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneReadRAW)) { subghz->scene_manager, SubGhzSceneReadRAW)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) { subghz->scene_manager, SubGhzSceneSaved)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);

View File

@ -4,8 +4,8 @@ void subghz_scene_saved_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(subghz_load_protocol_from_file(subghz)) { if(subghz_load_protocol_from_file(subghz)) {
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
} else { } else {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu);

View File

@ -1,63 +1,10 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <lib/subghz/protocols/keeloq.h> #include "../helpers/subghz_txrx_create_protocol_key.h"
#include <lib/subghz/protocols/secplus_v1.h>
#include <lib/subghz/protocols/secplus_v2.h>
#include <lib/subghz/blocks/math.h> #include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/protocol_items.h> #include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhzSetType" #define TAG "SubGhzSetType"
bool subghz_scene_set_type_submenu_gen_data_protocol(
void* context,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t frequency,
const char* preset_name) {
furi_assert(context);
SubGhz* subghz = context;
bool res = false;
subghz_preset_init(subghz, preset_name, frequency, NULL, 0);
subghz->txrx->decoder_result =
subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name);
if(subghz->txrx->decoder_result == NULL) {
furi_string_set(subghz->error_str, "Protocol not\nfound!");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
return false;
}
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
stream_clean(fff_data_stream);
if(subghz_protocol_decoder_base_serialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) !=
SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
if(!flipper_format_update_uint32(subghz->txrx->fff_data, "Bit", &bit, 1)) {
FURI_LOG_E(TAG, "Unable to update Bit");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(subghz->txrx->fff_data, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Key");
break;
}
res = true;
} while(false);
return res;
}
void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context; SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index); view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
@ -69,7 +16,13 @@ void subghz_scene_set_type_on_enter(void* context) {
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"Princeton_433", "Princeton_433",
SubmenuIndexPricenton, SubmenuIndexPricenton_433,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"Princeton_315",
SubmenuIndexPricenton_315,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item( submenu_add_item(
@ -108,10 +61,6 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexCAMETwee, SubmenuIndexCAMETwee,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
// submenu_add_item(
// subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
// submenu_add_item(
// subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"Gate TX_433", "Gate TX_433",
@ -172,94 +121,59 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
bool generated_protocol = false; bool generated_protocol = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
//ToDo Fix uint32_t key = (uint32_t)rand();
uint32_t key = subghz_random_serial();
switch(event.event) { switch(event.event) {
case SubmenuIndexPricenton: case SubmenuIndexPricenton_433:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol_and_te(
subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);
uint32_t te = 400; break;
flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); case SubmenuIndexPricenton_315:
generated_protocol = true; key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
} generated_protocol = subghz_txrx_gen_data_protocol_and_te(
subghz->txrx, "AM650", 315000000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);
break; break;
case SubmenuIndexNiceFlo12bit: case SubmenuIndexNiceFlo12bit:
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12);
generated_protocol = true;
}
break; break;
case SubmenuIndexNiceFlo24bit: case SubmenuIndexNiceFlo24bit:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAME12bit: case SubmenuIndexCAME12bit:
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAME24bit: case SubmenuIndexCAME24bit:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexLinear_300_00: case SubmenuIndexLinear_300_00:
key = (key & 0x3FF); key = (key & 0x3FF);
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { subghz->txrx, "AM650", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAMETwee: case SubmenuIndexCAMETwee:
key = (key & 0x0FFFFFF0); key = (key & 0x0FFFFFF0);
key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE);
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54, 433920000, "AM650")) { generated_protocol = subghz_txrx_gen_data_protocol(
generated_protocol = true; subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54);
}
break; break;
// case SubmenuIndexNeroSketch:
// /* code */
// break;
// case SubmenuIndexNeroRadio:
// /* code */
// break;
case SubmenuIndexGateTX: case SubmenuIndexGateTX:
key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24);
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexDoorHan_433_92: case SubmenuIndexDoorHan_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003);
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x0FFFFFFF,
0x2,
0x0003,
"DoorHan",
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) { if(!generated_protocol) {
furi_string_set( furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases."); subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
@ -267,23 +181,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
} }
break; break;
case SubmenuIndexDoorHan_315_00: case SubmenuIndexDoorHan_315_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003);
subghz_preset_init(subghz, "AM650", 315000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x0FFFFFFF,
0x2,
0x0003,
"DoorHan",
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) { if(!generated_protocol) {
furi_string_set( furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases."); subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
@ -291,86 +190,24 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
} }
break; break;
case SubmenuIndexLiftMaster_315_00: case SubmenuIndexLiftMaster_315_00:
while(!subghz_protocol_secplus_v1_check_fixed(key)) { generated_protocol =
key = subghz_random_serial(); subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 315000000);
}
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)key << 32 | 0xE6000000,
42,
315000000,
"AM650")) {
generated_protocol = true;
}
break; break;
case SubmenuIndexLiftMaster_390_00: case SubmenuIndexLiftMaster_390_00:
while(!subghz_protocol_secplus_v1_check_fixed(key)) { generated_protocol =
key = subghz_random_serial(); subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 390000000);
}
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)key << 32 | 0xE6000000,
42,
390000000,
"AM650")) {
generated_protocol = true;
}
break; break;
case SubmenuIndexSecPlus_v2_310_00: case SubmenuIndexSecPlus_v2_310_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 310000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 310000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
case SubmenuIndexSecPlus_v2_315_00: case SubmenuIndexSecPlus_v2_315_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 315000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 315000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
case SubmenuIndexSecPlus_v2_390_00: case SubmenuIndexSecPlus_v2_390_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 390000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 390000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
default: default:
return false; return false;

View File

@ -50,9 +50,10 @@ void subghz_scene_show_error_on_enter(void* context) {
bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzCustomEvent scene_state =
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError);
if(event.type == SceneManagerEventTypeBack) { if(event.type == SceneManagerEventTypeBack) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
return false; return false;
} else { } else {
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
@ -61,14 +62,12 @@ bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
return true; return true;
} else if(event.type == SceneManagerEventTypeCustom) { } else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneShowErrorOk) { if(event.event == SubGhzCustomEventSceneShowErrorOk) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
} }
return true; return true;
} else if(event.event == SubGhzCustomEventSceneShowErrorBack) { } else if(event.event == SubGhzCustomEventSceneShowErrorBack) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
//exit app //exit app
if(!scene_manager_previous_scene(subghz->scene_manager)) { if(!scene_manager_previous_scene(subghz->scene_manager)) {
scene_manager_stop(subghz->scene_manager); scene_manager_stop(subghz->scene_manager);

View File

@ -70,7 +70,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexReadRAW) { if(event.event == SubmenuIndexReadRAW) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
return true; return true;
} else if(event.event == SubmenuIndexRead) { } else if(event.event == SubmenuIndexRead) {

View File

@ -11,32 +11,24 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
bool subghz_scene_transmitter_update_data_show(void* context) { bool subghz_scene_transmitter_update_data_show(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
bool ret = false; bool ret = false;
if(subghz->txrx->decoder_result) { SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx);
FuriString* key_str;
FuriString* frequency_str;
FuriString* modulation_str;
key_str = furi_string_alloc(); if(decoder) {
frequency_str = furi_string_alloc(); FuriString* key_str = furi_string_alloc();
modulation_str = furi_string_alloc(); FuriString* frequency_str = furi_string_alloc();
uint8_t show_button = 0; FuriString* modulation_str = furi_string_alloc();
if(subghz_protocol_decoder_base_deserialize( if(subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) { decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) {
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); subghz_protocol_decoder_base_get_string(decoder, key_str);
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
SubGhzProtocolFlag_Send) {
show_button = 1;
}
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_view_transmitter_add_data_to_show( subghz_view_transmitter_add_data_to_show(
subghz->subghz_transmitter, subghz->subghz_transmitter,
furi_string_get_cstr(key_str), furi_string_get_cstr(key_str),
furi_string_get_cstr(frequency_str), furi_string_get_cstr(frequency_str),
furi_string_get_cstr(modulation_str), furi_string_get_cstr(modulation_str),
show_button); subghz_txrx_protocol_is_transmittable(subghz->txrx, false));
ret = true; ret = true;
} }
furi_string_free(frequency_str); furi_string_free(frequency_str);
@ -65,24 +57,16 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventViewTransmitterSendStart) { if(event.event == SubGhzCustomEventViewTransmitterSendStart) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz); if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
} subghz->state_notifications = SubGhzNotificationStateTx;
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz_scene_transmitter_update_data_show(subghz);
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { DOLPHIN_DEED(DolphinDeedSubGhzSend);
if(subghz_tx_start(subghz, subghz->txrx->fff_data)) {
subghz->state_notifications = SubGhzNotificationStateTx;
subghz_scene_transmitter_update_data_show(subghz);
DOLPHIN_DEED(DolphinDeedSubGhzSend);
}
} }
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) { } else if(event.event == SubGhzCustomEventViewTransmitterBack) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;

View File

@ -1,9 +1,6 @@
/* Abandon hope, all ye who enter here. */ /* Abandon hope, all ye who enter here. */
#include "subghz/types.h"
#include "subghz_i.h" #include "subghz_i.h"
#include <lib/toolbox/path.h>
#include <lib/subghz/protocols/protocol_items.h>
bool subghz_custom_event_callback(void* context, uint32_t event) { bool subghz_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
@ -49,16 +46,6 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context)
} }
} }
void subghz_blink_start(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_start_magenta);
}
void subghz_blink_stop(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_stop);
}
SubGhz* subghz_alloc() { SubGhz* subghz_alloc() {
SubGhz* subghz = malloc(sizeof(SubGhz)); SubGhz* subghz = malloc(sizeof(SubGhz));
@ -163,45 +150,18 @@ SubGhz* subghz_alloc() {
SubGhzViewIdStatic, SubGhzViewIdStatic,
subghz_test_static_get_view(subghz->subghz_test_static)); subghz_test_static_get_view(subghz->subghz_test_static));
//init setting //init threshold rssi
subghz->setting = subghz_setting_alloc(); subghz->threshold_rssi = subghz_threshold_rssi_alloc();
subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user"));
//init Worker & Protocol & History & KeyBoard subghz_unlock(subghz);
subghz->lock = SubGhzLockOff; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz->txrx = malloc(sizeof(SubGhzTxRx)); subghz->history = subghz_history_alloc();
subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); subghz->filter = SubGhzProtocolFlag_Decodable;
subghz->txrx->preset->name = furi_string_alloc();
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
subghz->txrx->txrx_state = SubGhzTxRxStateSleep; //init TxRx & History & KeyBoard
subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx = subghz_txrx_alloc();
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz);
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
subghz->txrx->history = subghz_history_alloc();
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->fff_data = flipper_format_string_alloc();
subghz->txrx->environment = subghz_environment_alloc();
subghz_environment_set_came_atomo_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
subghz->txrx->environment, (void*)&subghz_protocol_registry);
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
subghz->txrx->filter = SubGhzProtocolFlag_Decodable;
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
subghz_worker_set_overrun_callback(
subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->receiver);
//Init Error_str //Init Error_str
subghz->error_str = furi_string_alloc(); subghz->error_str = furi_string_alloc();
@ -219,7 +179,9 @@ void subghz_free(SubGhz* subghz) {
subghz->rpc_ctx = NULL; subghz->rpc_ctx = NULL;
} }
subghz_speaker_off(subghz); subghz_txrx_speaker_off(subghz->txrx);
subghz_txrx_stop(subghz->txrx);
subghz_txrx_sleep(subghz->txrx);
// Packet Test // Packet Test
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
@ -282,18 +244,14 @@ void subghz_free(SubGhz* subghz) {
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);
subghz->gui = NULL; subghz->gui = NULL;
//setting // threshold rssi
subghz_setting_free(subghz->setting); subghz_threshold_rssi_free(subghz->threshold_rssi);
//Worker & Protocol & History //Worker & Protocol & History
subghz_receiver_free(subghz->txrx->receiver); subghz_history_free(subghz->history);
subghz_environment_free(subghz->txrx->environment);
subghz_worker_free(subghz->txrx->worker); //TxRx
flipper_format_free(subghz->txrx->fff_data); subghz_txrx_free(subghz->txrx);
subghz_history_free(subghz->txrx->history);
furi_string_free(subghz->txrx->preset->name);
free(subghz->txrx->preset);
free(subghz->txrx);
//Error string //Error string
furi_string_free(subghz->error_str); furi_string_free(subghz->error_str);
@ -319,11 +277,6 @@ int32_t subghz_app(void* p) {
return 1; return 1;
} }
//Load database
bool load_database = subghz_environment_load_keystore(
subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
subghz_environment_load_keystore(
subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
// Check argument and run corresponding scene // Check argument and run corresponding scene
if(p && strlen(p)) { if(p && strlen(p)) {
uint32_t rpc_ctx = 0; uint32_t rpc_ctx = 0;
@ -340,9 +293,9 @@ int32_t subghz_app(void* p) {
if(subghz_key_load(subghz, p, true)) { if(subghz_key_load(subghz, p, true)) {
furi_string_set(subghz->file_path, (const char*)p); furi_string_set(subghz->file_path, (const char*)p);
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {
//Load Raw TX //Load Raw TX
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
} else { } else {
//Load transmitter TX //Load transmitter TX
@ -358,7 +311,7 @@ int32_t subghz_app(void* p) {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
if(load_database) { if(subghz_txrx_is_database_loaded(subghz->txrx)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
} else { } else {
scene_manager_set_scene_state( scene_manager_set_scene_state(

View File

@ -18,214 +18,42 @@
#define TAG "SubGhz" #define TAG "SubGhz"
void subghz_preset_init( void subghz_set_default_preset(SubGhz* subghz) {
void* context,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size) {
furi_assert(context);
SubGhz* subghz = context;
furi_string_set(subghz->txrx->preset->name, preset_name);
subghz->txrx->preset->frequency = frequency;
subghz->txrx->preset->data = preset_data;
subghz->txrx->preset->data_size = preset_data_size;
}
bool subghz_set_preset(SubGhz* subghz, const char* preset) {
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
furi_string_set(subghz->txrx->preset->name, "AM270");
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
furi_string_set(subghz->txrx->preset->name, "AM650");
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
furi_string_set(subghz->txrx->preset->name, "FM238");
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
furi_string_set(subghz->txrx->preset->name, "FM476");
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
furi_string_set(subghz->txrx->preset->name, "CUSTOM");
} else {
FURI_LOG_E(TAG, "Unknown preset");
return false;
}
return true;
}
void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation) {
furi_assert(subghz); furi_assert(subghz);
if(frequency != NULL) { subghz_txrx_set_preset(
furi_string_printf( subghz->txrx,
frequency, "AM650",
"%03ld.%02ld", subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)),
subghz->txrx->preset->frequency / 1000000 % 1000, NULL,
subghz->txrx->preset->frequency / 10000 % 100); 0);
}
if(modulation != NULL) {
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(subghz->txrx->preset->name));
}
} }
void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { void subghz_blink_start(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
furi_hal_subghz_reset(); notification_message(subghz->notifications, &sequence_blink_stop);
furi_hal_subghz_idle(); notification_message(subghz->notifications, &sequence_blink_start_magenta);
furi_hal_subghz_load_custom_preset(preset_data);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
} }
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { void subghz_blink_stop(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
if(!furi_hal_subghz_is_frequency_valid(frequency)) { notification_message(subghz->notifications, &sequence_blink_stop);
furi_crash("SubGhz: Incorrect RX frequency.");
}
furi_assert(
subghz->txrx->txrx_state != SubGhzTxRxStateRx &&
subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
subghz_speaker_on(subghz);
furi_hal_subghz_rx();
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker);
subghz_worker_start(subghz->txrx->worker);
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
return value;
}
static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
furi_assert(subghz);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect TX frequency.");
}
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_write(&gpio_cc1101_g0, false);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
bool ret = furi_hal_subghz_tx();
if(ret) {
subghz_speaker_on(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
}
return ret;
}
void subghz_idle(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
subghz_speaker_off(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_rx_end(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(subghz->txrx->worker)) {
subghz_worker_stop(subghz->txrx->worker);
furi_hal_subghz_stop_async_rx();
}
furi_hal_subghz_idle();
subghz_speaker_off(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_sleep(SubGhz* subghz) {
furi_assert(subghz);
furi_hal_subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
} }
bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
furi_assert(subghz); switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) {
case SubGhzTxRxStartTxStateErrorParserOthers:
dialog_message_show_storage_error(
subghz->dialogs, "Error in protocol\nparameters\ndescription");
break;
case SubGhzTxRxStartTxStateErrorOnlyRx:
subghz_dialog_message_show_only_rx(subghz);
break;
bool ret = false; default:
FuriString* temp_str; return true;
temp_str = furi_string_alloc(); break;
uint32_t repeat = 200;
do {
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, furi_string_get_cstr(temp_str));
if(subghz->txrx->transmitter) {
if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) ==
SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
} else {
FURI_LOG_E(
TAG,
"Unknown name preset \" %s \"",
furi_string_get_cstr(subghz->txrx->preset->name));
subghz_begin(
subghz, subghz_setting_get_preset_data_by_name(subghz->setting, "AM650"));
}
if(subghz->txrx->preset->frequency) {
ret = subghz_tx(subghz, subghz->txrx->preset->frequency);
} else {
ret = subghz_tx(subghz, 433920000);
}
if(ret) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, subghz->txrx->transmitter);
} else {
subghz_dialog_message_show_only_rx(subghz);
}
} else {
dialog_message_show_storage_error(
subghz->dialogs, "Error in protocol\nparameters\ndescription");
}
}
if(!ret) {
subghz_transmitter_free(subghz->txrx->transmitter);
if(subghz->txrx->txrx_state != SubGhzTxRxStateSleep) {
subghz_idle(subghz);
}
}
} while(false);
furi_string_free(temp_str);
return ret;
}
void subghz_tx_stop(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateTx);
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_transmitter_stop(subghz->txrx->transmitter);
subghz_transmitter_free(subghz->txrx->transmitter);
//if protocol dynamic then we save the last upload
if((subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) &&
(subghz_path_is_file(subghz->file_path))) {
subghz_save_protocol_to_file(
subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path));
} }
subghz_idle(subghz); return false;
subghz_speaker_off(subghz);
notification_message(subghz->notifications, &sequence_reset_red);
} }
void subghz_dialog_message_show_only_rx(SubGhz* subghz) { void subghz_dialog_message_show_only_rx(SubGhz* subghz) {
@ -254,11 +82,11 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); Stream* fff_data_stream =
flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx));
SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr;
FuriString* temp_str; FuriString* temp_str = furi_string_alloc();
temp_str = furi_string_alloc();
uint32_t temp_data32; uint32_t temp_data32;
do { do {
@ -281,6 +109,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
break; break;
} }
//Load frequency
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
FURI_LOG_E(TAG, "Missing Frequency"); FURI_LOG_E(TAG, "Missing Frequency");
break; break;
@ -291,58 +120,61 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
break; break;
} }
subghz->txrx->preset->frequency = temp_data32; //Load preset
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset"); FURI_LOG_E(TAG, "Missing Preset");
break; break;
} }
if(!subghz_set_preset(subghz, furi_string_get_cstr(temp_str))) { furi_string_set_str(
temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str)));
if(!strcmp(furi_string_get_cstr(temp_str), "")) {
break; break;
} }
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
//Todo add Custom_preset_module //Todo add Custom_preset_module
//delete preset if it already exists //delete preset if it already exists
subghz_setting_delete_custom_preset( subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str));
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name));
//load custom preset from file //load custom preset from file
if(!subghz_setting_load_custom_preset( if(!subghz_setting_load_custom_preset(
subghz->setting, setting, furi_string_get_cstr(temp_str), fff_data_file)) {
furi_string_get_cstr(subghz->txrx->preset->name),
fff_data_file)) {
FURI_LOG_E(TAG, "Missing Custom preset"); FURI_LOG_E(TAG, "Missing Custom preset");
break; break;
} }
} }
size_t preset_index = subghz_setting_get_inx_preset_by_name( size_t preset_index =
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));
subghz_preset_init( subghz_txrx_set_preset(
subghz, subghz->txrx,
furi_string_get_cstr(subghz->txrx->preset->name), furi_string_get_cstr(temp_str),
subghz->txrx->preset->frequency, temp_data32,
subghz_setting_get_preset_data(subghz->setting, preset_index), subghz_setting_get_preset_data(setting, preset_index),
subghz_setting_get_preset_data_size(subghz->setting, preset_index)); subghz_setting_get_preset_data_size(setting, preset_index));
//Load protocol
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol"); FURI_LOG_E(TAG, "Missing Protocol");
break; break;
} }
FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
//if RAW //if RAW
subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, file_path); subghz->load_type_file = SubGhzLoadTypeFileRaw;
subghz_protocol_raw_gen_fff_data(fff_data, file_path);
} else { } else {
subghz->load_type_file = SubGhzLoadTypeFileKey;
stream_copy_full( stream_copy_full(
flipper_format_get_raw_stream(fff_data_file), flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(subghz->txrx->fff_data)); flipper_format_get_raw_stream(fff_data));
} }
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( if(subghz_txrx_load_decoder_by_name_protocol(
subghz->txrx->receiver, furi_string_get_cstr(temp_str)); subghz->txrx, furi_string_get_cstr(temp_str))) {
if(subghz->txrx->decoder_result) {
SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data); subghz_txrx_get_decoder(subghz->txrx), fff_data);
if(status != SubGhzProtocolStatusOk) { if(status != SubGhzProtocolStatusOk) {
load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;
break; break;
@ -381,17 +213,18 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
} }
} }
SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) {
furi_assert(subghz);
return subghz->load_type_file;
}
bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
furi_assert(subghz); furi_assert(subghz);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FuriString* temp_str; FuriString* temp_str = furi_string_alloc();
FuriString* file_name; FuriString* file_name = furi_string_alloc();
FuriString* file_path; FuriString* file_path = furi_string_alloc();
temp_str = furi_string_alloc();
file_name = furi_string_alloc();
file_path = furi_string_alloc();
bool res = false; bool res = false;
@ -438,8 +271,7 @@ bool subghz_save_protocol_to_file(
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
bool saved = false; bool saved = false;
FuriString* file_dir; FuriString* file_dir = furi_string_alloc();
file_dir = furi_string_alloc();
path_extract_dirname(dev_file_name, file_dir); path_extract_dirname(dev_file_name, file_dir);
do { do {
@ -467,11 +299,21 @@ bool subghz_save_protocol_to_file(
return saved; return saved;
} }
void subghz_save_to_file(void* context) {
furi_assert(context);
SubGhz* subghz = context;
if(subghz_path_is_file(subghz->file_path)) {
subghz_save_protocol_to_file(
subghz,
subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path));
}
}
bool subghz_load_protocol_from_file(SubGhz* subghz) { bool subghz_load_protocol_from_file(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
FuriString* file_path; FuriString* file_path = furi_string_alloc();
file_path = furi_string_alloc();
DialogsFileBrowserOptions browser_options; DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
@ -551,92 +393,27 @@ bool subghz_path_is_file(FuriString* path) {
return furi_string_end_with(path, SUBGHZ_APP_EXTENSION); return furi_string_end_with(path, SUBGHZ_APP_EXTENSION);
} }
uint32_t subghz_random_serial(void) { void subghz_lock(SubGhz* subghz) {
return (uint32_t)rand();
}
void subghz_hopper_update(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
subghz->lock = SubGhzLockOn;
switch(subghz->txrx->hopper_state) {
case SubGhzHopperStateOFF:
case SubGhzHopperStatePause:
return;
case SubGhzHopperStateRSSITimeOut:
if(subghz->txrx->hopper_timeout != 0) {
subghz->txrx->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(subghz->txrx->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
subghz->txrx->hopper_timeout = 10;
subghz->txrx->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(subghz->txrx->hopper_idx_frequency <
subghz_setting_get_hopper_frequency_count(subghz->setting) - 1) {
subghz->txrx->hopper_idx_frequency++;
} else {
subghz->txrx->hopper_idx_frequency = 0;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
};
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
subghz_receiver_reset(subghz->txrx->receiver);
subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency(
subghz->setting, subghz->txrx->hopper_idx_frequency);
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
} }
void subghz_speaker_on(SubGhz* subghz) { void subghz_unlock(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_acquire(30)) { subghz->lock = SubGhzLockOff;
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
} else {
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
} }
void subghz_speaker_off(SubGhz* subghz) { bool subghz_is_locked(SubGhz* subghz) {
if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { return (subghz->lock == SubGhzLockOn);
furi_hal_subghz_set_async_mirror_pin(NULL);
furi_hal_speaker_release();
if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown)
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
} }
void subghz_speaker_mute(SubGhz* subghz) { void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { subghz->rx_key_state = state;
furi_hal_subghz_set_async_mirror_pin(NULL);
}
}
} }
void subghz_speaker_unmute(SubGhz* subghz) { SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { return subghz->rx_key_state;
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
}
}
} }

View File

@ -25,10 +25,6 @@
#include <gui/modules/widget.h> #include <gui/modules/widget.h>
#include <subghz/scenes/subghz_scene.h> #include <subghz/scenes/subghz_scene.h>
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include "subghz_history.h" #include "subghz_history.h"
@ -37,34 +33,12 @@
#include "rpc/rpc_app.h" #include "rpc/rpc_app.h"
#include "helpers/subghz_threshold_rssi.h"
#include "helpers/subghz_txrx.h"
#define SUBGHZ_MAX_LEN_NAME 64 #define SUBGHZ_MAX_LEN_NAME 64
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
SubGhzProtocolFlag filter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SubGhzRadioPreset* preset;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state;
SubGhzHopperState hopper_state;
SubGhzSpeakerState speaker_state;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
SubGhzRxKeyState rx_key_state;
float raw_threshold_rssi;
uint8_t raw_threshold_rssi_low_count;
};
typedef struct SubGhzTxRx SubGhzTxRx;
struct SubGhz { struct SubGhz {
Gui* gui; Gui* gui;
NotificationApp* notifications; NotificationApp* notifications;
@ -93,47 +67,43 @@ struct SubGhz {
SubGhzTestStatic* subghz_test_static; SubGhzTestStatic* subghz_test_static;
SubGhzTestCarrier* subghz_test_carrier; SubGhzTestCarrier* subghz_test_carrier;
SubGhzTestPacket* subghz_test_packet; SubGhzTestPacket* subghz_test_packet;
FuriString* error_str;
SubGhzSetting* setting;
SubGhzLock lock;
SubGhzProtocolFlag filter;
FuriString* error_str;
SubGhzLock lock;
SubGhzThresholdRssi* threshold_rssi;
SubGhzRxKeyState rx_key_state;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzLoadTypeFile load_type_file;
void* rpc_ctx; void* rpc_ctx;
}; };
void subghz_preset_init( void subghz_set_default_preset(SubGhz* subghz);
void* context, void subghz_blink_start(SubGhz* subghz);
const char* preset_name, void subghz_blink_stop(SubGhz* subghz);
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size);
bool subghz_set_preset(SubGhz* subghz, const char* preset);
void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation);
void subghz_begin(SubGhz* subghz, uint8_t* preset_data);
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency);
void subghz_rx_end(SubGhz* subghz);
void subghz_sleep(SubGhz* subghz);
void subghz_blink_start(SubGhz* instance);
void subghz_blink_stop(SubGhz* instance);
bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format);
void subghz_tx_stop(SubGhz* subghz);
void subghz_dialog_message_show_only_rx(SubGhz* subghz); void subghz_dialog_message_show_only_rx(SubGhz* subghz);
bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog);
bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len);
bool subghz_save_protocol_to_file( bool subghz_save_protocol_to_file(
SubGhz* subghz, SubGhz* subghz,
FlipperFormat* flipper_format, FlipperFormat* flipper_format,
const char* dev_file_name); const char* dev_file_name);
void subghz_save_to_file(void* context);
bool subghz_load_protocol_from_file(SubGhz* subghz); bool subghz_load_protocol_from_file(SubGhz* subghz);
bool subghz_rename_file(SubGhz* subghz); bool subghz_rename_file(SubGhz* subghz);
bool subghz_file_available(SubGhz* subghz); bool subghz_file_available(SubGhz* subghz);
bool subghz_delete_file(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz);
void subghz_file_name_clear(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz);
bool subghz_path_is_file(FuriString* path); bool subghz_path_is_file(FuriString* path);
uint32_t subghz_random_serial(void); SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz);
void subghz_hopper_update(SubGhz* subghz);
void subghz_speaker_on(SubGhz* subghz); void subghz_lock(SubGhz* subghz);
void subghz_speaker_off(SubGhz* subghz); void subghz_unlock(SubGhz* subghz);
void subghz_speaker_mute(SubGhz* subghz); bool subghz_is_locked(SubGhz* subghz);
void subghz_speaker_unmute(SubGhz* subghz);
void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state);
SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz);

View File

@ -12,7 +12,7 @@
#define MENU_ITEMS 4u #define MENU_ITEMS 4u
#define UNLOCK_CNT 3 #define UNLOCK_CNT 3
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct { typedef struct {
FuriString* item_str; FuriString* item_str;
@ -44,7 +44,7 @@ typedef enum {
} SubGhzViewReceiverBarShow; } SubGhzViewReceiverBarShow;
struct SubGhzViewReceiver { struct SubGhzViewReceiver {
SubGhzLock lock; bool lock;
uint8_t lock_count; uint8_t lock_count;
FuriTimer* timer; FuriTimer* timer;
View* view; View* view;
@ -70,20 +70,21 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
instance->view, instance->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ {
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0; model->u_rssi = 0;
} else { } else {
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
} }
}, },
true); true);
} }
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
if(lock == SubGhzLockOn) {
subghz_receiver->lock = lock; if(lock == true) {
subghz_receiver->lock = true;
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
@ -280,7 +281,7 @@ static void subghz_view_receiver_timer_callback(void* context) {
subghz_receiver->callback( subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else { } else {
subghz_receiver->lock = SubGhzLockOff; subghz_receiver->lock = false;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
} }
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
@ -290,7 +291,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context; SubGhzViewReceiver* subghz_receiver = context;
if(subghz_receiver->lock == SubGhzLockOn) { if(subghz_receiver->lock == true) {
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
@ -310,7 +311,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowUnlock; }, { model->bar_show = SubGhzViewReceiverBarShowUnlock; },
true); true);
//subghz_receiver->lock = SubGhzLockOff; //subghz_receiver->lock = false;
furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650));
} }
@ -394,7 +395,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() {
// View allocation and configuration // View allocation and configuration
subghz_receiver->view = view_alloc(); subghz_receiver->view = view_alloc();
subghz_receiver->lock = SubGhzLockOff; subghz_receiver->lock = false;
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
view_allocate_model( view_allocate_model(
subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel));

View File

@ -10,7 +10,7 @@ typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* contex
void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool keyboard);
void subghz_view_receiver_set_callback( void subghz_view_receiver_set_callback(
SubGhzViewReceiver* subghz_receiver, SubGhzViewReceiver* subghz_receiver,

View File

@ -60,10 +60,10 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra
furi_assert(instance); furi_assert(instance);
uint8_t u_rssi = 0; uint8_t u_rssi = 0;
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
u_rssi = 0; u_rssi = 0;
} else { } else {
u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
} }
with_view_model( with_view_model(
@ -261,9 +261,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod
uint8_t x = 118; uint8_t x = 118;
uint8_t y = 48; uint8_t y = 48;
if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {
uint8_t x = 118; uint8_t x = 118;
y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
uint8_t width = 3; uint8_t width = 3;
for(uint8_t i = 0; i < x; i += width * 2) { for(uint8_t i = 0; i < x; i += width * 2) {

View File

@ -3,7 +3,7 @@
#include <gui/view.h> #include <gui/view.h>
#include "../helpers/subghz_custom_event.h" #include "../helpers/subghz_custom_event.h"
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct SubGhzReadRAW SubGhzReadRAW; typedef struct SubGhzReadRAW SubGhzReadRAW;

View File

@ -14,7 +14,7 @@ typedef struct {
FuriString* frequency_str; FuriString* frequency_str;
FuriString* preset_str; FuriString* preset_str;
FuriString* key_str; FuriString* key_str;
uint8_t show_button; bool show_button;
} SubGhzViewTransmitterModel; } SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback( void subghz_view_transmitter_set_callback(
@ -32,7 +32,7 @@ void subghz_view_transmitter_add_data_to_show(
const char* key_str, const char* key_str,
const char* frequency_str, const char* frequency_str,
const char* preset_str, const char* preset_str,
uint8_t show_button) { bool show_button) {
furi_assert(subghz_transmitter); furi_assert(subghz_transmitter);
with_view_model( with_view_model(
subghz_transmitter->view, subghz_transmitter->view,
@ -104,7 +104,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_string_reset(model->frequency_str); furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str); furi_string_reset(model->preset_str);
furi_string_reset(model->key_str); furi_string_reset(model->key_str);
model->show_button = 0; model->show_button = false;
}, },
false); false);
return false; return false;

View File

@ -23,4 +23,4 @@ void subghz_view_transmitter_add_data_to_show(
const char* key_str, const char* key_str,
const char* frequency_str, const char* frequency_str,
const char* preset_str, const char* preset_str,
uint8_t show_button); bool show_button);

View File

@ -220,11 +220,9 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) {
UNUSED(context); UNUSED(context);
if(!furi_string_cmp(args, "0")) { if(!furi_string_cmp(args, "0")) {
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
loader_update_menu();
printf("Debug disabled."); printf("Debug disabled.");
} else if(!furi_string_cmp(args, "1")) { } else if(!furi_string_cmp(args, "1")) {
furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); furi_hal_rtc_set_flag(FuriHalRtcFlagDebug);
loader_update_menu();
printf("Debug enabled."); printf("Debug enabled.");
} else { } else {
cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args));

View File

@ -6,6 +6,8 @@
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <cli/cli.h>
#include <cli/cli_vcp.h>
#include "animations/animation_manager.h" #include "animations/animation_manager.h"
#include "desktop/scenes/desktop_scene.h" #include "desktop/scenes/desktop_scene.h"
@ -14,7 +16,7 @@
#include "desktop/views/desktop_view_pin_input.h" #include "desktop/views/desktop_view_pin_input.h"
#include "desktop/views/desktop_view_pin_timeout.h" #include "desktop/views/desktop_view_pin_timeout.h"
#include "desktop_i.h" #include "desktop_i.h"
#include "helpers/pin_lock.h" #include "helpers/pin.h"
#include "helpers/slideshow_filename.h" #include "helpers/slideshow_filename.h"
#define TAG "Desktop" #define TAG "Desktop"
@ -132,6 +134,14 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) {
} }
void desktop_lock(Desktop* desktop) { void desktop_lock(Desktop* desktop) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
if(desktop->settings.pin_code.length) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_close(cli);
furi_record_close(RECORD_CLI);
}
desktop_auto_lock_inhibit(desktop); desktop_auto_lock_inhibit(desktop);
scene_manager_set_scene_state( scene_manager_set_scene_state(
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
@ -147,6 +157,14 @@ void desktop_unlock(Desktop* desktop) {
desktop_view_locked_unlock(desktop->locked_view); desktop_view_locked_unlock(desktop->locked_view);
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
desktop_auto_lock_arm(desktop); desktop_auto_lock_arm(desktop);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
furi_hal_rtc_set_pin_fails(0);
if(desktop->settings.pin_code.length) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
}
} }
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
@ -290,11 +308,14 @@ Desktop* desktop_alloc() {
desktop->auto_lock_timer = desktop->auto_lock_timer =
furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop);
furi_record_create(RECORD_DESKTOP, desktop);
return desktop; return desktop;
} }
void desktop_free(Desktop* desktop) { void desktop_free(Desktop* desktop) {
furi_assert(desktop); furi_assert(desktop);
furi_check(furi_record_destroy(RECORD_DESKTOP));
furi_pubsub_unsubscribe( furi_pubsub_unsubscribe(
loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription);
@ -352,6 +373,16 @@ static bool desktop_check_file_flag(const char* flag_path) {
return exists; return exists;
} }
bool desktop_api_is_locked(Desktop* instance) {
furi_assert(instance);
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
}
void desktop_api_unlock(Desktop* instance) {
furi_assert(instance);
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked);
}
int32_t desktop_srv(void* p) { int32_t desktop_srv(void* p) {
UNUSED(p); UNUSED(p);
@ -375,14 +406,12 @@ int32_t desktop_srv(void* p) {
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
desktop_pin_lock_init(&desktop->settings); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
desktop_lock(desktop);
if(!desktop_pin_lock_is_locked()) { } else {
if(!loader_is_locked(desktop->loader)) { if(!loader_is_locked(desktop->loader)) {
desktop_auto_lock_arm(desktop); desktop_auto_lock_arm(desktop);
} }
} else {
desktop_lock(desktop);
} }
if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) {

View File

@ -1,3 +1,9 @@
#pragma once #pragma once
typedef struct Desktop Desktop; typedef struct Desktop Desktop;
#define RECORD_DESKTOP "desktop"
bool desktop_api_is_locked(Desktop* instance);
void desktop_api_unlock(Desktop* instance);

View File

@ -8,7 +8,7 @@
#include <toolbox/saved_struct.h> #include <toolbox/saved_struct.h>
#include <storage/storage.h> #include <storage/storage.h>
#define DESKTOP_SETTINGS_VER (6) #define DESKTOP_SETTINGS_VER (7)
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
#define DESKTOP_SETTINGS_MAGIC (0x17) #define DESKTOP_SETTINGS_MAGIC (0x17)
@ -52,7 +52,6 @@ typedef struct {
FavoriteApp favorite_primary; FavoriteApp favorite_primary;
FavoriteApp favorite_secondary; FavoriteApp favorite_secondary;
PinCode pin_code; PinCode pin_code;
uint8_t is_locked;
uint32_t auto_lock_delay_ms; uint32_t auto_lock_delay_ms;
uint8_t dummy_mode; uint8_t dummy_mode;
} DesktopSettings; } DesktopSettings;

View File

@ -0,0 +1,74 @@
#include "pin.h"
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stddef.h>
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include "../desktop_i.h"
static const NotificationSequence sequence_pin_fail = {
&message_display_backlight_on,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
&message_delay_250,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
NULL,
};
static const uint8_t desktop_helpers_fails_timeout[] = {
0,
0,
0,
0,
30,
60,
90,
120,
150,
180,
/* +60 for every next fail */
};
void desktop_pin_lock_error_notify() {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_pin_fail);
furi_record_close(RECORD_NOTIFICATION);
}
uint32_t desktop_pin_lock_get_fail_timeout() {
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
uint32_t pin_timeout = 0;
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
if(pin_fails <= max_index) {
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
} else {
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
}
return pin_timeout;
}
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) {
furi_assert(pin_code1);
furi_assert(pin_code2);
bool result = false;
if(pin_code1->length == pin_code2->length) {
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
}
return result;
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "../desktop.h"
#include <desktop/desktop_settings.h>
void desktop_pin_lock_error_notify();
uint32_t desktop_pin_lock_get_fail_timeout();
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2);

View File

@ -1,140 +0,0 @@
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stddef.h>
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include "../helpers/pin_lock.h"
#include "../desktop_i.h"
#include <cli/cli.h>
#include <cli/cli_vcp.h>
static const NotificationSequence sequence_pin_fail = {
&message_display_backlight_on,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
&message_delay_250,
&message_red_255,
&message_vibro_on,
&message_delay_100,
&message_vibro_off,
&message_red_0,
NULL,
};
static const uint8_t desktop_helpers_fails_timeout[] = {
0,
0,
0,
0,
30,
60,
90,
120,
150,
180,
/* +60 for every next fail */
};
void desktop_pin_lock_error_notify() {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_pin_fail);
furi_record_close(RECORD_NOTIFICATION);
}
uint32_t desktop_pin_lock_get_fail_timeout() {
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
uint32_t pin_timeout = 0;
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
if(pin_fails <= max_index) {
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
} else {
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
}
return pin_timeout;
}
void desktop_pin_lock(DesktopSettings* settings) {
furi_assert(settings);
furi_hal_rtc_set_pin_fails(0);
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_close(cli);
furi_record_close(RECORD_CLI);
settings->is_locked = 1;
DESKTOP_SETTINGS_SAVE(settings);
}
void desktop_pin_unlock(DesktopSettings* settings) {
furi_assert(settings);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
settings->is_locked = 0;
DESKTOP_SETTINGS_SAVE(settings);
}
void desktop_pin_lock_init(DesktopSettings* settings) {
furi_assert(settings);
if(settings->pin_code.length > 0) {
if(settings->is_locked == 1) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
} else {
if(desktop_pin_lock_is_locked()) {
settings->is_locked = 1;
DESKTOP_SETTINGS_SAVE(settings);
}
}
} else {
furi_hal_rtc_set_pin_fails(0);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
}
if(desktop_pin_lock_is_locked()) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_close(cli);
furi_record_close(RECORD_CLI);
}
}
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) {
bool result = false;
if(desktop_pins_are_equal(pin_set, pin_entered)) {
furi_hal_rtc_set_pin_fails(0);
result = true;
} else {
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
furi_hal_rtc_set_pin_fails(pin_fails + 1);
result = false;
}
return result;
}
bool desktop_pin_lock_is_locked() {
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
}
bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) {
furi_assert(pin_code1);
furi_assert(pin_code2);
bool result = false;
if(pin_code1->length == pin_code2->length) {
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
}
return result;
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "../desktop.h"
#include <desktop/desktop_settings.h>
void desktop_pin_lock_error_notify();
uint32_t desktop_pin_lock_get_fail_timeout();
void desktop_pin_lock(DesktopSettings* settings);
void desktop_pin_unlock(DesktopSettings* settings);
bool desktop_pin_lock_is_locked();
void desktop_pin_lock_init(DesktopSettings* settings);
bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered);
bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2);

View File

@ -10,7 +10,7 @@
#include "../views/desktop_view_lock_menu.h" #include "../views/desktop_view_lock_menu.h"
#include "desktop_scene_i.h" #include "desktop_scene_i.h"
#include "desktop_scene.h" #include "desktop_scene.h"
#include "../helpers/pin_lock.h" #include "../helpers/pin.h"
#define TAG "DesktopSceneLock" #define TAG "DesktopSceneLock"
@ -25,7 +25,6 @@ void desktop_scene_lock_menu_on_enter(void* context) {
DESKTOP_SETTINGS_LOAD(&desktop->settings); DESKTOP_SETTINGS_LOAD(&desktop->settings);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0);
desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode);
desktop_lock_menu_set_stealth_mode_state( desktop_lock_menu_set_stealth_mode_state(
desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode));
@ -44,7 +43,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
if(check_pin_changed) { if(check_pin_changed) {
DESKTOP_SETTINGS_LOAD(&desktop->settings); DESKTOP_SETTINGS_LOAD(&desktop->settings);
if(desktop->settings.pin_code.length > 0) { if(desktop->settings.pin_code.length > 0) {
desktop_lock_menu_set_pin_state(desktop->lock_menu, true);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
} }
} }
@ -55,21 +53,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
desktop_lock(desktop); desktop_lock(desktop);
consumed = true; consumed = true;
break; break;
case DesktopLockMenuEventPinLock:
if(desktop->settings.pin_code.length > 0) {
desktop_pin_lock(&desktop->settings);
desktop_lock(desktop);
} else {
LoaderStatus status =
loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG);
if(status == LoaderStatusOk) {
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1);
} else {
FURI_LOG_E(TAG, "Unable to start desktop settings");
}
}
consumed = true;
break;
case DesktopLockMenuEventDummyModeOn: case DesktopLockMenuEventDummyModeOn:
desktop_set_dummy_mode_state(desktop, true); desktop_set_dummy_mode_state(desktop, true);
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(

View File

@ -7,7 +7,7 @@
#include "../desktop.h" #include "../desktop.h"
#include "../desktop_i.h" #include "../desktop_i.h"
#include "../helpers/pin_lock.h" #include "../helpers/pin.h"
#include "../animations/animation_manager.h" #include "../animations/animation_manager.h"
#include "../views/desktop_events.h" #include "../views/desktop_events.h"
#include "../views/desktop_view_pin_input.h" #include "../views/desktop_view_pin_input.h"
@ -45,7 +45,7 @@ void desktop_scene_locked_on_enter(void* context) {
bool switch_to_timeout_scene = false; bool switch_to_timeout_scene = false;
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
if(state == SCENE_LOCKED_FIRST_ENTER) { if(state == SCENE_LOCKED_FIRST_ENTER) {
bool pin_locked = desktop_pin_lock_is_locked(); bool pin_locked = desktop->settings.pin_code.length > 0;
view_port_enabled_set(desktop->lock_icon_viewport, true); view_port_enabled_set(desktop->lock_icon_viewport, true);
Gui* gui = furi_record_open(RECORD_GUI); Gui* gui = furi_record_open(RECORD_GUI);
gui_set_lockdown(gui, true); gui_set_lockdown(gui, true);

View File

@ -106,10 +106,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DesktopMainEventOpenMenu: case DesktopMainEventOpenMenu: {
loader_show_menu(); Loader* loader = furi_record_open(RECORD_LOADER);
loader_show_menu(loader);
furi_record_close(RECORD_LOADER);
consumed = true; consumed = true;
break; } break;
case DesktopMainEventOpenLockMenu: case DesktopMainEventOpenLockMenu:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);

View File

@ -12,7 +12,7 @@
#include "../animations/animation_manager.h" #include "../animations/animation_manager.h"
#include "../views/desktop_events.h" #include "../views/desktop_events.h"
#include "../views/desktop_view_pin_input.h" #include "../views/desktop_view_pin_input.h"
#include "../helpers/pin_lock.h" #include "../helpers/pin.h"
#include "desktop_scene.h" #include "desktop_scene.h"
#include "desktop_scene_i.h" #include "desktop_scene_i.h"
@ -54,9 +54,11 @@ static void desktop_scene_pin_input_back_callback(void* context) {
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
Desktop* desktop = (Desktop*)context; Desktop* desktop = (Desktop*)context;
if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) { if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) {
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
} else { } else {
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
furi_hal_rtc_set_pin_fails(pin_fails + 1);
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); desktop->view_dispatcher, DesktopPinInputEventUnlockFailed);
} }
@ -126,7 +128,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
break; break;
case DesktopPinInputEventUnlocked: case DesktopPinInputEventUnlocked:
desktop_pin_unlock(&desktop->settings);
desktop_unlock(desktop); desktop_unlock(desktop);
consumed = true; consumed = true;
break; break;

View File

@ -31,7 +31,6 @@ typedef enum {
DesktopDebugEventExit, DesktopDebugEventExit,
DesktopLockMenuEventLock, DesktopLockMenuEventLock,
DesktopLockMenuEventPinLock,
DesktopLockMenuEventDummyModeOn, DesktopLockMenuEventDummyModeOn,
DesktopLockMenuEventDummyModeOff, DesktopLockMenuEventDummyModeOff,
DesktopLockMenuEventStealthModeOn, DesktopLockMenuEventStealthModeOn,

View File

@ -65,13 +65,16 @@ void desktop_debug_render(Canvas* canvas, void* model) {
version_get_builddate(ver)); version_get_builddate(ver));
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);
uint16_t api_major, api_minor;
furi_hal_info_get_api_version(&api_major, &api_minor);
snprintf( snprintf(
buffer, buffer,
sizeof(buffer), sizeof(buffer),
"%s%s [%s] %s", "%s%s [%d.%d] %s",
version_get_dirty_flag(ver) ? "[!] " : "", version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver), version_get_githash(ver),
version_get_gitbranchnum(ver), api_major,
api_minor,
c2_ver ? c2_ver->StackTypeString : "<none>"); c2_ver ? c2_ver->StackTypeString : "<none>");
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);

View File

@ -23,14 +23,6 @@ void desktop_lock_menu_set_callback(
lock_menu->context = context; lock_menu->context = context;
} }
void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set) {
with_view_model(
lock_menu->view,
DesktopLockMenuViewModel * model,
{ model->pin_is_set = pin_is_set; },
true);
}
void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) {
with_view_model( with_view_model(
lock_menu->view, lock_menu->view,
@ -102,7 +94,6 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) {
bool consumed = false; bool consumed = false;
bool dummy_mode = false; bool dummy_mode = false;
bool stealth_mode = false; bool stealth_mode = false;
bool pin_is_set = false;
bool update = false; bool update = false;
with_view_model( with_view_model(
@ -131,15 +122,12 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) {
idx = model->idx; idx = model->idx;
dummy_mode = model->dummy_mode; dummy_mode = model->dummy_mode;
stealth_mode = model->stealth_mode; stealth_mode = model->stealth_mode;
pin_is_set = model->pin_is_set;
}, },
update); update);
if(event->key == InputKeyOk) { if(event->key == InputKeyOk) {
if((idx == DesktopLockMenuIndexLock)) { if((idx == DesktopLockMenuIndexLock)) {
if((pin_is_set) && (event->type == InputTypeShort)) { if((event->type == InputTypeShort)) {
lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context);
} else if((pin_is_set == false) && (event->type == InputTypeShort)) {
lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context);
} }
} else if(idx == DesktopLockMenuIndexStealth) { } else if(idx == DesktopLockMenuIndexStealth) {

View File

@ -17,7 +17,6 @@ struct DesktopLockMenuView {
typedef struct { typedef struct {
uint8_t idx; uint8_t idx;
bool pin_is_set;
bool dummy_mode; bool dummy_mode;
bool stealth_mode; bool stealth_mode;
} DesktopLockMenuViewModel; } DesktopLockMenuViewModel;
@ -28,7 +27,6 @@ void desktop_lock_menu_set_callback(
void* context); void* context);
View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set);
void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode);
void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode);
void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx);

View File

@ -1,79 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "../desktop_i.h"
#include "desktop_view_pin_setup_done.h"
struct DesktopViewPinSetupDone {
View* view;
DesktopViewPinSetupDoneDoneCallback callback;
void* context;
};
static void desktop_view_pin_done_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
UNUSED(model);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(
canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols");
canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29);
elements_button_right(canvas, "Next");
}
static bool desktop_view_pin_done_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopViewPinSetupDone* instance = context;
bool consumed = false;
if((event->key == InputKeyRight) && (event->type == InputTypeShort)) {
instance->callback(instance->context);
consumed = true;
}
return consumed;
}
void desktop_view_pin_done_set_callback(
DesktopViewPinSetupDone* instance,
DesktopViewPinSetupDoneDoneCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
DesktopViewPinSetupDone* desktop_view_pin_done_alloc() {
DesktopViewPinSetupDone* view = malloc(sizeof(DesktopViewPinSetupDone));
view->view = view_alloc();
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_view_pin_done_draw);
view_set_input_callback(view->view, desktop_view_pin_done_input);
return view;
}
void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) {
furi_assert(instance);
return instance->view;
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone;
typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*);
void desktop_view_pin_done_set_callback(
DesktopViewPinSetupDone* instance,
DesktopViewPinSetupDoneDoneCallback callback,
void* context);
DesktopViewPinSetupDone* desktop_view_pin_done_alloc();
void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance);
View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance);

View File

@ -154,6 +154,8 @@ Menu* menu_alloc() {
void menu_free(Menu* menu) { void menu_free(Menu* menu) {
furi_assert(menu); furi_assert(menu);
menu_reset(menu); menu_reset(menu);
with_view_model(
menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false);
view_free(menu->view); view_free(menu->view);
free(menu); free(menu);
} }

View File

@ -19,19 +19,16 @@ void view_tie_icon_animation(View* view, IconAnimation* icon_animation) {
void view_set_draw_callback(View* view, ViewDrawCallback callback) { void view_set_draw_callback(View* view, ViewDrawCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(view->draw_callback == NULL);
view->draw_callback = callback; view->draw_callback = callback;
} }
void view_set_input_callback(View* view, ViewInputCallback callback) { void view_set_input_callback(View* view, ViewInputCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(view->input_callback == NULL);
view->input_callback = callback; view->input_callback = callback;
} }
void view_set_custom_callback(View* view, ViewCustomCallback callback) { void view_set_custom_callback(View* view, ViewCustomCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(callback);
view->custom_callback = callback; view->custom_callback = callback;
} }
@ -62,7 +59,6 @@ void view_set_update_callback_context(View* view, void* context) {
void view_set_context(View* view, void* context) { void view_set_context(View* view, void* context) {
furi_assert(view); furi_assert(view);
furi_assert(context);
view->context = context; view->context = context;
} }

View File

@ -5,6 +5,7 @@ App(
entry_point="loader_srv", entry_point="loader_srv",
cdefines=["SRV_LOADER"], cdefines=["SRV_LOADER"],
requires=["gui"], requires=["gui"],
provides=["loader_start"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=90, order=90,
sdk_headers=[ sdk_headers=[
@ -12,3 +13,11 @@ App(
"firmware_api/firmware_api.h", "firmware_api/firmware_api.h",
], ],
) )
App(
appid="loader_start",
apptype=FlipperAppType.STARTUP,
entry_point="loader_on_system_start",
requires=["loader"],
order=90,
)

View File

@ -6,6 +6,8 @@
/* Generated table */ /* Generated table */
#include <firmware_api_table.h> #include <firmware_api_table.h>
#include <furi_hal_info.h>
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
constexpr HashtableApiInterface elf_api_interface{ constexpr HashtableApiInterface elf_api_interface{
@ -19,3 +21,8 @@ constexpr HashtableApiInterface elf_api_interface{
}; };
const ElfApiInterface* const firmware_api_interface = &elf_api_interface; const ElfApiInterface* const firmware_api_interface = &elf_api_interface;
extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {
*major = elf_api_interface.api_version_major;
*minor = elf_api_interface.api_version_minor;
}

View File

@ -1,76 +1,114 @@
#include "applications.h" #include "loader.h"
#include <furi.h>
#include "loader/loader.h"
#include "loader_i.h" #include "loader_i.h"
#include "loader_menu.h"
#include <applications.h>
#include <furi_hal.h>
#define TAG "LoaderSrv" #define TAG "Loader"
#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
// api
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) LoaderStatus loader_start(Loader* loader, const char* name, const char* args) {
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) LoaderMessage message;
LoaderMessageLoaderStatusResult result;
static Loader* loader_instance = NULL; message.type = LoaderMessageTypeStartByName;
message.start.name = name;
static bool message.start.args = args;
loader_start_application(const FlipperApplication* application, const char* arguments) { message.api_lock = api_lock_alloc_locked();
loader_instance->application = application; message.status_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
furi_assert(loader_instance->application_arguments == NULL); api_lock_wait_unlock_and_free(message.api_lock);
if(arguments && strlen(arguments) > 0) { return result.value;
loader_instance->application_arguments = strdup(arguments);
}
FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name);
FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
if(mode > FuriHalRtcHeapTrackModeNone) {
furi_thread_enable_heap_trace(loader_instance->application_thread);
} else {
furi_thread_disable_heap_trace(loader_instance->application_thread);
}
furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name);
furi_thread_set_appid(
loader_instance->application_thread, loader_instance->application->appid);
furi_thread_set_stack_size(
loader_instance->application_thread, loader_instance->application->stack_size);
furi_thread_set_context(
loader_instance->application_thread, loader_instance->application_arguments);
furi_thread_set_callback(
loader_instance->application_thread, loader_instance->application->app);
furi_thread_start(loader_instance->application_thread);
return true;
} }
static void loader_menu_callback(void* _ctx, uint32_t index) { bool loader_lock(Loader* loader) {
UNUSED(index); LoaderMessage message;
const FlipperApplication* application = _ctx; LoaderMessageBoolResult result;
message.type = LoaderMessageTypeLock;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
furi_assert(application->app); void loader_unlock(Loader* loader) {
furi_assert(application->name); LoaderMessage message;
message.type = LoaderMessageTypeUnlock;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
if(!loader_lock(loader_instance)) { bool loader_is_locked(Loader* loader) {
FURI_LOG_E(TAG, "Loader is locked"); LoaderMessage message;
return; LoaderMessageBoolResult result;
message.type = LoaderMessageTypeIsLocked;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
void loader_show_menu(Loader* loader) {
LoaderMessage message;
message.type = LoaderMessageTypeShowMenu;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
FuriPubSub* loader_get_pubsub(Loader* loader) {
furi_assert(loader);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
// also the loader instance cannot be obtained until the pubsub is created
return loader->pubsub;
}
// callbacks
static void loader_menu_closed_callback(void* context) {
Loader* loader = context;
LoaderMessage message;
message.type = LoaderMessageTypeMenuClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
static void loader_menu_click_callback(const char* name, void* context) {
Loader* loader = context;
loader_start(loader, name, NULL);
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* loader = context;
LoaderEvent event;
if(thread_state == FuriThreadStateRunning) {
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader->pubsub, &event);
} else if(thread_state == FuriThreadStateStopped) {
LoaderMessage message;
message.type = LoaderMessageTypeAppClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
event.type = LoaderEventTypeApplicationStopped;
furi_pubsub_publish(loader->pubsub, &event);
} }
loader_start_application(application, NULL);
} }
static void loader_submenu_callback(void* context, uint32_t index) { // implementation
UNUSED(index);
uint32_t view_id = (uint32_t)context;
view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id);
}
static void loader_cli_print_usage() { static Loader* loader_alloc() {
printf("Usage:\r\n"); Loader* loader = malloc(sizeof(Loader));
printf("loader <cmd> <args>\r\n"); loader->pubsub = furi_pubsub_alloc();
printf("Cmd list:\r\n"); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
printf("\tlist\t - List available applications\r\n"); loader->loader_menu = NULL;
printf("\topen <Application Name:string>\t - Open application by name\r\n"); loader->app.args = NULL;
printf("\tinfo\t - Show loader state\r\n"); loader->app.name = NULL;
loader->app.thread = NULL;
loader->app.insomniac = false;
return loader;
} }
static FlipperApplication const* loader_find_application_by_name_in_list( static FlipperApplication const* loader_find_application_by_name_in_list(
@ -85,7 +123,7 @@ static FlipperApplication const* loader_find_application_by_name_in_list(
return NULL; return NULL;
} }
const FlipperApplication* loader_find_application_by_name(const char* name) { static const FlipperApplication* loader_find_application_by_name(const char* name) {
const FlipperApplication* application = NULL; const FlipperApplication* application = NULL;
application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
if(!application) { if(!application) {
@ -100,346 +138,168 @@ const FlipperApplication* loader_find_application_by_name(const char* name) {
return application; return application;
} }
static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { static void
UNUSED(cli); loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) {
if(loader_is_locked(instance)) { FURI_LOG_I(TAG, "Starting %s", app->name);
if(instance->application) {
furi_assert(instance->application->name); // store args
printf("Can't start, %s application is running", instance->application->name); furi_assert(loader->app.args == NULL);
} else { if(args && strlen(args) > 0) {
printf("Can't start, furi application is running"); loader->app.args = strdup(args);
}
return;
} }
FuriString* application_name; // store name
application_name = furi_string_alloc(); furi_assert(loader->app.name == NULL);
loader->app.name = strdup(app->name);
do { // setup app thread
if(!args_read_probably_quoted_string_and_trim(args, application_name)) { loader->app.thread =
printf("No application provided\r\n"); furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args);
break; furi_thread_set_appid(loader->app.thread, app->appid);
}
const FlipperApplication* application = // setup heap trace
loader_find_application_by_name(furi_string_get_cstr(application_name)); FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
if(!application) { if(mode > FuriHalRtcHeapTrackModeNone) {
printf("%s doesn't exists\r\n", furi_string_get_cstr(application_name)); furi_thread_enable_heap_trace(loader->app.thread);
break;
}
furi_string_trim(args);
if(!loader_start_application(application, furi_string_get_cstr(args))) {
printf("Can't start, furi application is running");
return;
} else {
// We must to increment lock counter to keep balance
// TODO: rewrite whole thing, it's complex as hell
FURI_CRITICAL_ENTER();
instance->lock_count++;
FURI_CRITICAL_EXIT();
}
} while(false);
furi_string_free(application_name);
}
static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) {
UNUSED(cli);
UNUSED(args);
UNUSED(instance);
printf("Applications:\r\n");
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_APPS[i].name);
}
}
static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) {
UNUSED(cli);
UNUSED(args);
if(!loader_is_locked(instance)) {
printf("No application is running\r\n");
} else { } else {
printf("Running application: "); furi_thread_disable_heap_trace(loader->app.thread);
if(instance->application) { }
furi_assert(instance->application->name);
printf("%s\r\n", instance->application->name); // setup insomnia
} else { if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) {
printf("unknown\r\n"); furi_hal_power_insomnia_enter();
} loader->app.insomniac = true;
} else {
loader->app.insomniac = false;
}
// setup app thread callbacks
furi_thread_set_state_context(loader->app.thread, loader);
furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback);
// start app thread
furi_thread_start(loader->app.thread);
}
// process messages
static void loader_do_menu_show(Loader* loader) {
if(!loader->loader_menu) {
loader->loader_menu = loader_menu_alloc();
loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader);
loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader);
loader_menu_start(loader->loader_menu);
} }
} }
static void loader_cli(Cli* cli, FuriString* args, void* _ctx) { static void loader_do_menu_closed(Loader* loader) {
furi_assert(_ctx); if(loader->loader_menu) {
Loader* instance = _ctx; loader_menu_stop(loader->loader_menu);
loader_menu_free(loader->loader_menu);
FuriString* cmd; loader->loader_menu = NULL;
cmd = furi_string_alloc(); }
do {
if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "list") == 0) {
loader_cli_list(cli, args, instance);
break;
}
if(furi_string_cmp_str(cmd, "open") == 0) {
loader_cli_open(cli, args, instance);
break;
}
if(furi_string_cmp_str(cmd, "info") == 0) {
loader_cli_info(cli, args, instance);
break;
}
loader_cli_print_usage();
} while(false);
furi_string_free(cmd);
} }
LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { static bool loader_do_is_locked(Loader* loader) {
UNUSED(instance); return loader->app.thread != NULL;
furi_assert(name); }
const FlipperApplication* application = loader_find_application_by_name(name); static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) {
if(loader_do_is_locked(loader)) {
if(!application) {
FURI_LOG_E(TAG, "Can't find application with name %s", name);
return LoaderStatusErrorUnknownApp;
}
if(!loader_lock(loader_instance)) {
FURI_LOG_E(TAG, "Loader is locked");
return LoaderStatusErrorAppStarted; return LoaderStatusErrorAppStarted;
} }
if(!loader_start_application(application, args)) { const FlipperApplication* app = loader_find_application_by_name(name);
return LoaderStatusErrorInternal;
if(!app) {
return LoaderStatusErrorUnknownApp;
} }
loader_start_internal_app(loader, app, args);
return LoaderStatusOk; return LoaderStatusOk;
} }
bool loader_lock(Loader* instance) { static bool loader_do_lock(Loader* loader) {
FURI_CRITICAL_ENTER(); if(loader->app.thread) {
bool result = false; return false;
if(instance->lock_count == 0) {
instance->lock_count++;
result = true;
}
FURI_CRITICAL_EXIT();
return result;
}
void loader_unlock(Loader* instance) {
FURI_CRITICAL_ENTER();
if(instance->lock_count > 0) instance->lock_count--;
FURI_CRITICAL_EXIT();
}
bool loader_is_locked(const Loader* instance) {
return instance->lock_count > 0;
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* instance = context;
LoaderEvent event;
if(thread_state == FuriThreadStateRunning) {
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader_instance->pubsub, &event);
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
furi_hal_power_insomnia_enter();
}
} else if(thread_state == FuriThreadStateStopped) {
FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
if(loader_instance->application_arguments) {
free(loader_instance->application_arguments);
loader_instance->application_arguments = NULL;
}
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
furi_hal_power_insomnia_exit();
}
loader_unlock(instance);
event.type = LoaderEventTypeApplicationStopped;
furi_pubsub_publish(loader_instance->pubsub, &event);
}
}
static uint32_t loader_hide_menu(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static uint32_t loader_back_to_primary_menu(void* context) {
furi_assert(context);
Submenu* submenu = context;
submenu_set_selected_item(submenu, 0);
return LoaderMenuViewPrimary;
}
static Loader* loader_alloc() {
Loader* instance = malloc(sizeof(Loader));
instance->application_thread = furi_thread_alloc();
furi_thread_set_state_context(instance->application_thread, instance);
furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback);
instance->pubsub = furi_pubsub_alloc();
#ifdef SRV_CLI
instance->cli = furi_record_open(RECORD_CLI);
cli_add_command(
instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance);
#else
UNUSED(loader_cli);
#endif
instance->loader_thread = furi_thread_get_current_id();
// Gui
instance->gui = furi_record_open(RECORD_GUI);
instance->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
// Primary menu
instance->primary_menu = menu_alloc();
view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu);
view_dispatcher_add_view(
instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu));
// Settings menu
instance->settings_menu = submenu_alloc();
view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu);
view_set_previous_callback(
submenu_get_view(instance->settings_menu), loader_back_to_primary_menu);
view_dispatcher_add_view(
instance->view_dispatcher,
LoaderMenuViewSettings,
submenu_get_view(instance->settings_menu));
view_dispatcher_enable_queue(instance->view_dispatcher);
return instance;
}
static void loader_free(Loader* instance) {
furi_assert(instance);
if(instance->cli) {
furi_record_close(RECORD_CLI);
} }
furi_pubsub_free(instance->pubsub); loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;
return true;
furi_thread_free(instance->application_thread);
menu_free(loader_instance->primary_menu);
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary);
submenu_free(loader_instance->settings_menu);
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings);
view_dispatcher_free(loader_instance->view_dispatcher);
furi_record_close(RECORD_GUI);
free(instance);
instance = NULL;
} }
static void loader_build_menu() { static void loader_do_unlock(Loader* loader) {
FURI_LOG_I(TAG, "Building main menu"); furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
size_t i; loader->app.thread = NULL;
for(i = 0; i < FLIPPER_APPS_COUNT; i++) { }
menu_add_item(
loader_instance->primary_menu, static void loader_do_app_closed(Loader* loader) {
FLIPPER_APPS[i].name, furi_assert(loader->app.thread);
FLIPPER_APPS[i].icon, FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
i, if(loader->app.args) {
loader_menu_callback, free(loader->app.args);
(void*)&FLIPPER_APPS[i]); loader->app.args = NULL;
} }
menu_add_item(
loader_instance->primary_menu,
"Settings",
&A_Settings_14,
i++,
loader_submenu_callback,
(void*)LoaderMenuViewSettings);
}
static void loader_build_submenu() { if(loader->app.insomniac) {
FURI_LOG_I(TAG, "Building settings menu"); furi_hal_power_insomnia_exit();
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
submenu_add_item(
loader_instance->settings_menu,
FLIPPER_SETTINGS_APPS[i].name,
i,
loader_menu_callback,
(void*)&FLIPPER_SETTINGS_APPS[i]);
} }
free(loader->app.name);
loader->app.name = NULL;
furi_thread_join(loader->app.thread);
furi_thread_free(loader->app.thread);
loader->app.thread = NULL;
} }
void loader_show_menu() { // app
furi_assert(loader_instance);
furi_thread_flags_set(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU);
}
void loader_update_menu() {
menu_reset(loader_instance->primary_menu);
loader_build_menu();
}
int32_t loader_srv(void* p) { int32_t loader_srv(void* p) {
UNUSED(p); UNUSED(p);
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
FURI_LOG_I(TAG, "Executing system start hooks"); FURI_LOG_I(TAG, "Executing system start hooks");
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
FLIPPER_ON_SYSTEM_START[i](); FLIPPER_ON_SYSTEM_START[i]();
} }
FURI_LOG_I(TAG, "Starting");
loader_instance = loader_alloc();
loader_build_menu();
loader_build_submenu();
FURI_LOG_I(TAG, "Started");
furi_record_create(RECORD_LOADER, loader_instance);
if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) {
loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL);
} }
while(1) { LoaderMessage message;
uint32_t flags = while(true) {
furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { switch(message.type) {
menu_set_selected_item(loader_instance->primary_menu, 0); case LoaderMessageTypeStartByName:
view_dispatcher_switch_to_view( message.status_value->value =
loader_instance->view_dispatcher, LoaderMenuViewPrimary); loader_do_start_by_name(loader, message.start.name, message.start.args);
view_dispatcher_run(loader_instance->view_dispatcher); api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeShowMenu:
loader_do_menu_show(loader);
break;
case LoaderMessageTypeMenuClosed:
loader_do_menu_closed(loader);
break;
case LoaderMessageTypeIsLocked:
message.bool_value->value = loader_do_is_locked(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeAppClosed:
loader_do_app_closed(loader);
break;
case LoaderMessageTypeLock:
message.bool_value->value = loader_do_lock(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeUnlock:
loader_do_unlock(loader);
}
} }
} }
furi_record_destroy(RECORD_LOADER);
loader_free(loader_instance);
return 0; return 0;
} }
FuriPubSub* loader_get_pubsub(Loader* instance) {
return instance->pubsub;
}

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <furi.h>
#include <core/pubsub.h>
#include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -43,17 +41,14 @@ bool loader_lock(Loader* instance);
void loader_unlock(Loader* instance); void loader_unlock(Loader* instance);
/** Get loader lock status */ /** Get loader lock status */
bool loader_is_locked(const Loader* instance); bool loader_is_locked(Loader* instance);
/** Show primary loader */ /** Show primary loader */
void loader_show_menu(); void loader_show_menu(Loader* instance);
/** Show primary loader */
void loader_update_menu();
/** Show primary loader */ /** Show primary loader */
FuriPubSub* loader_get_pubsub(Loader* instance); FuriPubSub* loader_get_pubsub(Loader* instance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -0,0 +1,117 @@
#include <furi.h>
#include <cli/cli.h>
#include <applications.h>
#include <lib/toolbox/args.h>
#include "loader.h"
static void loader_cli_print_usage() {
printf("Usage:\r\n");
printf("loader <cmd> <args>\r\n");
printf("Cmd list:\r\n");
printf("\tlist\t - List available applications\r\n");
printf("\topen <Application Name:string>\t - Open application by name\r\n");
printf("\tinfo\t - Show loader state\r\n");
}
static void loader_cli_list() {
printf("Applications:\r\n");
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_APPS[i].name);
}
printf("Settings:\r\n");
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_SETTINGS_APPS[i].name);
}
}
static void loader_cli_info(Loader* loader) {
if(!loader_is_locked(loader)) {
printf("No application is running\r\n");
} else {
// TODO: print application name ???
printf("Application is running\r\n");
}
}
static void loader_cli_open(FuriString* args, Loader* loader) {
FuriString* app_name = furi_string_alloc();
do {
if(!args_read_probably_quoted_string_and_trim(args, app_name)) {
printf("No application provided\r\n");
break;
}
furi_string_trim(args);
const char* args_str = furi_string_get_cstr(args);
if(strlen(args_str) == 0) {
args_str = NULL;
}
const char* app_name_str = furi_string_get_cstr(app_name);
LoaderStatus status = loader_start(loader, app_name_str, args_str);
switch(status) {
case LoaderStatusOk:
break;
case LoaderStatusErrorAppStarted:
printf("Can't start, application is running");
break;
case LoaderStatusErrorUnknownApp:
printf("%s doesn't exists\r\n", app_name_str);
break;
case LoaderStatusErrorInternal:
printf("Internal error\r\n");
break;
}
} while(false);
furi_string_free(app_name);
}
static void loader_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
Loader* loader = furi_record_open(RECORD_LOADER);
FuriString* cmd;
cmd = furi_string_alloc();
do {
if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "list") == 0) {
loader_cli_list();
break;
}
if(furi_string_cmp_str(cmd, "open") == 0) {
loader_cli_open(args, loader);
break;
}
if(furi_string_cmp_str(cmd, "info") == 0) {
loader_cli_info(loader);
break;
}
loader_cli_print_usage();
} while(false);
furi_string_free(cmd);
furi_record_close(RECORD_LOADER);
}
void loader_on_system_start() {
#ifdef SRV_CLI
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
furi_record_close(RECORD_CLI);
#else
UNUSED(loader_cli);
#endif
}

View File

@ -1,39 +1,56 @@
#include "loader.h" #pragma once
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <toolbox/api_lock.h>
#include <core/pubsub.h> #include "loader.h"
#include <cli/cli.h> #include "loader_menu.h"
#include <lib/toolbox/args.h>
#include <gui/view_dispatcher.h> typedef struct {
char* args;
#include <gui/modules/menu.h> char* name;
#include <gui/modules/submenu.h> FuriThread* thread;
bool insomniac;
#include <applications.h> } LoaderAppData;
#include <assets_icons.h>
struct Loader { struct Loader {
FuriThreadId loader_thread;
const FlipperApplication* application;
FuriThread* application_thread;
char* application_arguments;
Cli* cli;
Gui* gui;
ViewDispatcher* view_dispatcher;
Menu* primary_menu;
Submenu* settings_menu;
volatile uint8_t lock_count;
FuriPubSub* pubsub; FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderMenu* loader_menu;
LoaderAppData app;
}; };
typedef enum { typedef enum {
LoaderMenuViewPrimary, LoaderMessageTypeStartByName,
LoaderMenuViewSettings, LoaderMessageTypeAppClosed,
} LoaderMenuView; LoaderMessageTypeShowMenu,
LoaderMessageTypeMenuClosed,
LoaderMessageTypeLock,
LoaderMessageTypeUnlock,
LoaderMessageTypeIsLocked,
} LoaderMessageType;
typedef struct {
const char* name;
const char* args;
} LoaderMessageStartByName;
typedef struct {
LoaderStatus value;
} LoaderMessageLoaderStatusResult;
typedef struct {
bool value;
} LoaderMessageBoolResult;
typedef struct {
FuriApiLock api_lock;
LoaderMessageType type;
union {
LoaderMessageStartByName start;
};
union {
LoaderMessageLoaderStatusResult* status_value;
LoaderMessageBoolResult* bool_value;
};
} LoaderMessage;

View File

@ -0,0 +1,187 @@
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/menu.h>
#include <gui/modules/submenu.h>
#include <assets_icons.h>
#include <applications.h>
#include "loader_menu.h"
#define TAG "LoaderMenu"
struct LoaderMenu {
Gui* gui;
ViewDispatcher* view_dispatcher;
Menu* primary_menu;
Submenu* settings_menu;
void (*closed_callback)(void*);
void* closed_callback_context;
void (*click_callback)(const char*, void*);
void* click_callback_context;
FuriThread* thread;
};
typedef enum {
LoaderMenuViewPrimary,
LoaderMenuViewSettings,
} LoaderMenuView;
static int32_t loader_menu_thread(void* p);
LoaderMenu* loader_menu_alloc() {
LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu));
loader_menu->gui = furi_record_open(RECORD_GUI);
loader_menu->view_dispatcher = view_dispatcher_alloc();
loader_menu->primary_menu = menu_alloc();
loader_menu->settings_menu = submenu_alloc();
loader_menu->thread = NULL;
return loader_menu;
}
void loader_menu_free(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
// check if thread is running
furi_assert(!loader_menu->thread);
submenu_free(loader_menu->settings_menu);
menu_free(loader_menu->primary_menu);
view_dispatcher_free(loader_menu->view_dispatcher);
furi_record_close(RECORD_GUI);
free(loader_menu);
}
void loader_menu_start(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
furi_assert(!loader_menu->thread);
loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu);
furi_thread_start(loader_menu->thread);
}
void loader_menu_stop(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
furi_assert(loader_menu->thread);
view_dispatcher_stop(loader_menu->view_dispatcher);
furi_thread_join(loader_menu->thread);
furi_thread_free(loader_menu->thread);
loader_menu->thread = NULL;
}
void loader_menu_set_closed_callback(
LoaderMenu* loader_menu,
void (*callback)(void*),
void* context) {
loader_menu->closed_callback = callback;
loader_menu->closed_callback_context = context;
}
void loader_menu_set_click_callback(
LoaderMenu* loader_menu,
void (*callback)(const char*, void*),
void* context) {
loader_menu->click_callback = callback;
loader_menu->click_callback_context = context;
}
static void loader_menu_callback(void* context, uint32_t index) {
LoaderMenu* loader_menu = context;
const char* name = FLIPPER_APPS[index].name;
if(loader_menu->click_callback) {
loader_menu->click_callback(name, loader_menu->click_callback_context);
}
}
static void loader_menu_settings_menu_callback(void* context, uint32_t index) {
LoaderMenu* loader_menu = context;
const char* name = FLIPPER_SETTINGS_APPS[index].name;
if(loader_menu->click_callback) {
loader_menu->click_callback(name, loader_menu->click_callback_context);
}
}
static void loader_menu_switch_to_settings(void* context, uint32_t index) {
UNUSED(index);
LoaderMenu* loader_menu = context;
view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings);
}
static uint32_t loader_menu_switch_to_primary(void* context) {
UNUSED(context);
return LoaderMenuViewPrimary;
}
static uint32_t loader_menu_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static void loader_menu_build_menu(LoaderMenu* loader_menu) {
size_t i;
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
menu_add_item(
loader_menu->primary_menu,
FLIPPER_APPS[i].name,
FLIPPER_APPS[i].icon,
i,
loader_menu_callback,
(void*)loader_menu);
}
menu_add_item(
loader_menu->primary_menu,
"Settings",
&A_Settings_14,
i++,
loader_menu_switch_to_settings,
loader_menu);
};
static void loader_menu_build_submenu(LoaderMenu* loader_menu) {
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
submenu_add_item(
loader_menu->settings_menu,
FLIPPER_SETTINGS_APPS[i].name,
i,
loader_menu_settings_menu_callback,
loader_menu);
}
}
static int32_t loader_menu_thread(void* p) {
LoaderMenu* loader_menu = p;
furi_assert(loader_menu);
loader_menu_build_menu(loader_menu);
loader_menu_build_submenu(loader_menu);
view_dispatcher_attach_to_gui(
loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen);
// Primary menu
View* primary_view = menu_get_view(loader_menu->primary_menu);
view_set_context(primary_view, loader_menu->primary_menu);
view_set_previous_callback(primary_view, loader_menu_exit);
view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view);
// Settings menu
View* settings_view = submenu_get_view(loader_menu->settings_menu);
view_set_context(settings_view, loader_menu->settings_menu);
view_set_previous_callback(settings_view, loader_menu_switch_to_primary);
view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view);
view_dispatcher_enable_queue(loader_menu->view_dispatcher);
view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary);
// run view dispatcher
view_dispatcher_run(loader_menu->view_dispatcher);
view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary);
view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings);
if(loader_menu->closed_callback) {
loader_menu->closed_callback(loader_menu->closed_callback_context);
}
return 0;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <furi.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct LoaderMenu LoaderMenu;
LoaderMenu* loader_menu_alloc();
void loader_menu_free(LoaderMenu* loader_menu);
void loader_menu_start(LoaderMenu* loader_menu);
void loader_menu_stop(LoaderMenu* loader_menu);
void loader_menu_set_closed_callback(
LoaderMenu* loader_menu,
void (*callback)(void*),
void* context);
void loader_menu_set_click_callback(
LoaderMenu* loader_menu,
void (*callback)(const char*, void*),
void* context);
#ifdef __cplusplus
}
#endif

View File

@ -57,6 +57,10 @@ static RpcSystemCallbacks rpc_systems[] = {
.alloc = rpc_system_property_alloc, .alloc = rpc_system_property_alloc,
.free = NULL, .free = NULL,
}, },
{
.alloc = rpc_desktop_alloc,
.free = rpc_desktop_free,
},
}; };
struct RpcSession { struct RpcSession {
@ -326,31 +330,35 @@ static int32_t rpc_session_worker(void* context) {
return 0; return 0;
} }
static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { static void rpc_session_thread_pending_callback(void* context, uint32_t arg) {
furi_assert(context); UNUSED(arg);
RpcSession* session = (RpcSession*)context; RpcSession* session = (RpcSession*)context;
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
if(rpc_systems[i].free) {
(rpc_systems[i].free)(session->system_contexts[i]);
}
}
free(session->system_contexts);
free(session->decoded_message);
RpcHandlerDict_clear(session->handlers);
furi_stream_buffer_free(session->stream);
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
if(session->terminated_callback) {
session->terminated_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
furi_mutex_free(session->callbacks_mutex);
furi_thread_join(session->thread);
furi_thread_free(session->thread);
free(session);
}
static void rpc_session_thread_state_callback(FuriThreadState thread_state, void* context) {
if(thread_state == FuriThreadStateStopped) { if(thread_state == FuriThreadStateStopped) {
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0);
if(rpc_systems[i].free) {
rpc_systems[i].free(session->system_contexts[i]);
}
}
free(session->system_contexts);
free(session->decoded_message);
RpcHandlerDict_clear(session->handlers);
furi_stream_buffer_free(session->stream);
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
if(session->terminated_callback) {
session->terminated_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
furi_mutex_free(session->callbacks_mutex);
furi_thread_free(session->thread);
free(session);
} }
} }
@ -385,7 +393,7 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) {
session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
furi_thread_set_state_context(session->thread, session); furi_thread_set_state_context(session->thread, session);
furi_thread_set_state_callback(session->thread, rpc_session_free_callback); furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback);
furi_thread_start(session->thread); furi_thread_start(session->thread);

View File

@ -0,0 +1,73 @@
#include "flipper.pb.h"
#include "rpc_i.h"
#include <desktop/desktop.h>
#include "desktop.pb.h"
#define TAG "RpcDesktop"
typedef struct {
RpcSession* session;
Desktop* desktop;
} RpcDesktop;
static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_desktop_is_locked_request_tag);
FURI_LOG_D(TAG, "IsLockedRequest");
RpcDesktop* rpc_desktop = context;
RpcSession* session = rpc_desktop->session;
PB_CommandStatus ret = desktop_api_is_locked(rpc_desktop->desktop) ? PB_CommandStatus_OK :
PB_CommandStatus_ERROR;
rpc_send_and_release_empty(session, request->command_id, ret);
}
static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_desktop_unlock_request_tag);
FURI_LOG_D(TAG, "UnlockRequest");
RpcDesktop* rpc_desktop = context;
RpcSession* session = rpc_desktop->session;
desktop_api_unlock(rpc_desktop->desktop);
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
}
void* rpc_desktop_alloc(RpcSession* session) {
furi_assert(session);
RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop));
rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP);
rpc_desktop->session = session;
RpcHandler rpc_handler = {
.message_handler = NULL,
.decode_submessage = NULL,
.context = rpc_desktop,
};
rpc_handler.message_handler = rpc_desktop_on_is_locked_request;
rpc_add_handler(session, PB_Main_desktop_is_locked_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_desktop_on_unlock_request;
rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler);
return rpc_desktop;
}
void rpc_desktop_free(void* context) {
furi_assert(context);
RpcDesktop* rpc_desktop = context;
furi_assert(rpc_desktop->desktop);
furi_record_close(RECORD_DESKTOP);
rpc_desktop->session = NULL;
free(rpc_desktop);
}

View File

@ -36,6 +36,9 @@ void* rpc_system_gpio_alloc(RpcSession* session);
void rpc_system_gpio_free(void* ctx); void rpc_system_gpio_free(void* ctx);
void* rpc_system_property_alloc(RpcSession* session); void* rpc_system_property_alloc(RpcSession* session);
void* rpc_desktop_alloc(RpcSession* session);
void rpc_desktop_free(void* ctx);
void rpc_debug_print_message(const PB_Main* message); void rpc_debug_print_message(const PB_Main* message);
void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size);

View File

@ -803,6 +803,7 @@ void storage_file_free(File* file) {
} }
FuriPubSub* storage_get_pubsub(Storage* storage) { FuriPubSub* storage_get_pubsub(Storage* storage) {
furi_assert(storage);
return storage->pubsub; return storage->pubsub;
} }

Some files were not shown because too many files have changed in this diff Show More