Merge remote-tracking branch 'origin/dev' into release-candidate
This commit is contained in:
		
						commit
						508dbe4984
					
				
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -56,7 +56,7 @@ | ||||
| 
 | ||||
| # Lib | ||||
| /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/infrared/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| /lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
|  | ||||
							
								
								
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -4,7 +4,6 @@ on: | ||||
|   push: | ||||
|     branches: | ||||
|       - dev | ||||
|       - "release*" | ||||
|     tags: | ||||
|       - '*' | ||||
|   pull_request: | ||||
| @ -19,7 +18,7 @@ jobs: | ||||
|     runs-on: [self-hosted,FlipperZeroShell] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout code' | ||||
|         uses: actions/checkout@v3 | ||||
| @ -61,7 +60,7 @@ jobs: | ||||
|       - name: 'Bundle scripts' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         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' | ||||
|         run: | | ||||
| @ -167,7 +166,7 @@ jobs: | ||||
|         target: [f7, f18] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout code' | ||||
|         uses: actions/checkout@v3 | ||||
| @ -194,12 +193,14 @@ jobs: | ||||
|           TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ | ||||
|           ./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 "hw-target-code=$TARGET" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: Deploy uFBT with SDK | ||||
|         uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 | ||||
|         with: | ||||
|           task: setup | ||||
|           sdk-file: ${{ steps.build-fw.outputs.sdk-file }} | ||||
|           sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }} | ||||
| 
 | ||||
|       - name: Build test app with SDK | ||||
|         run: | | ||||
| @ -207,7 +208,7 @@ jobs: | ||||
|           cd testapp | ||||
|           ufbt create APPID=testapp | ||||
|           ufbt | ||||
|        | ||||
| 
 | ||||
|       - name: Build example & external apps with uFBT | ||||
|         run: | | ||||
|           for appdir in 'applications/external' 'applications/examples'; do | ||||
|  | ||||
| @ -4,7 +4,6 @@ on: | ||||
|   push: | ||||
|     branches: | ||||
|       - dev | ||||
|       - "release*" | ||||
|     tags: | ||||
|       - '*' | ||||
|   pull_request: | ||||
| @ -19,7 +18,7 @@ jobs: | ||||
|     runs-on: [self-hosted,FlipperZeroShell] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout code' | ||||
|         uses: actions/checkout@v3 | ||||
| @ -64,7 +63,7 @@ jobs: | ||||
|           else | ||||
|             echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; | ||||
|           fi | ||||
|    | ||||
| 
 | ||||
|       - name: 'Check C++ code formatting' | ||||
|         id: syntax_check_cpp | ||||
|         if: always() | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/merge_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/merge_report.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,7 +13,7 @@ jobs: | ||||
|     runs-on: [self-hosted,FlipperZeroShell] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout code' | ||||
|         uses: actions/checkout@v3 | ||||
|  | ||||
							
								
								
									
										3
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							| @ -4,7 +4,6 @@ on: | ||||
|   push: | ||||
|     branches: | ||||
|       - dev | ||||
|       - "release*" | ||||
|     tags: | ||||
|       - '*' | ||||
|   pull_request: | ||||
| @ -20,7 +19,7 @@ jobs: | ||||
|     runs-on: [self-hosted, FlipperZeroShell] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout code' | ||||
|         uses: actions/checkout@v3 | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,7 +13,7 @@ jobs: | ||||
|     runs-on: [self-hosted, FlipperZeroUnitTest] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v3 | ||||
|  | ||||
							
								
								
									
										6
									
								
								.github/workflows/updater_test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/updater_test.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,13 +13,13 @@ jobs: | ||||
|     runs-on: [self-hosted, FlipperZeroUpdaterTest] | ||||
|     steps: | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 1 | ||||
|           submodules: false           | ||||
|           submodules: false | ||||
|           ref: ${{ github.event.pull_request.head.sha }} | ||||
| 
 | ||||
|       - 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 | ||||
| 
 | ||||
|       - name: 'Wipe workspace' | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;  | ||||
|         run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; | ||||
| 
 | ||||
|       - name: 'Checkout latest release' | ||||
|         uses: actions/checkout@v3 | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,7 @@ | ||||
| *.swp | ||||
| *.swo | ||||
| *.gdb_history | ||||
| *.old | ||||
| 
 | ||||
| 
 | ||||
| # LSP | ||||
|  | ||||
							
								
								
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -1,9 +1,6 @@ | ||||
| [submodule "lib/mlib"] | ||||
| 	path = lib/mlib | ||||
| 	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"] | ||||
| 	path = lib/littlefs | ||||
| 	url = https://github.com/littlefs-project/littlefs.git | ||||
| @ -34,3 +31,12 @@ | ||||
| [submodule "lib/heatshrink"] | ||||
| 	path = lib/heatshrink | ||||
| 	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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										47
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							| @ -11,11 +11,10 @@ | ||||
|             "args": { | ||||
|                 "useSingleResult": true, | ||||
|                 "env": { | ||||
|                     "PATH": "${workspaceFolder};${env:PATH}", | ||||
|                     "FBT_QUIET": 1 | ||||
|                     "PATH": "${workspaceFolder}${command:extension.commandvariable.envListSep}${env:PATH}" | ||||
|                 }, | ||||
|                 "command": "fbt get_blackmagic", | ||||
|                 "description": "Get Blackmagic device", | ||||
|                 "command": "fbt -s get_blackmagic", | ||||
|                 "description": "Get Blackmagic device" | ||||
|             } | ||||
|         } | ||||
|     ], | ||||
| @ -28,20 +27,21 @@ | ||||
|             "type": "cortex-debug", | ||||
|             "servertype": "openocd", | ||||
|             "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, | ||||
|             // you have to comment out the following line. | ||||
|             "rtos": "FreeRTOS", | ||||
|             "configFiles": [ | ||||
|                 "interface/stlink.cfg", | ||||
|                 "./debug/stm32wbx.cfg", | ||||
|                 "./scripts/debug/stm32wbx.cfg", | ||||
|             ], | ||||
|             "postAttachCommands": [ | ||||
|                 "source scripts/debug/flipperversion.py", | ||||
|                 "fw-version", | ||||
|                 // "compare-sections", | ||||
|                 "source debug/flipperapps.py", | ||||
|                 "source scripts/debug/flipperapps.py", | ||||
|                 "fap-set-debug-elf-root build/latest/.extapps", | ||||
|                 // "source debug/FreeRTOS/FreeRTOS.py", | ||||
|                 // "svd_load debug/STM32WB55_CM4.svd" | ||||
|                 // "source scripts/debug/FreeRTOS/FreeRTOS.py", | ||||
|             ] | ||||
|             // "showDevDebugOutput": "raw", | ||||
|         }, | ||||
| @ -53,14 +53,16 @@ | ||||
|             "type": "cortex-debug", | ||||
|             "servertype": "external", | ||||
|             "gdbTarget": "${input:BLACKMAGIC}", | ||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", | ||||
|             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||
|             "rtos": "FreeRTOS", | ||||
|             "postAttachCommands": [ | ||||
|                 "monitor swdp_scan", | ||||
|                 "attach 1", | ||||
|                 "set confirm 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", | ||||
|                 // "compare-sections", | ||||
|             ] | ||||
| @ -75,10 +77,12 @@ | ||||
|             "servertype": "jlink", | ||||
|             "interface": "swd", | ||||
|             "device": "STM32WB55RG", | ||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", | ||||
|             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||
|             "rtos": "FreeRTOS", | ||||
|             "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", | ||||
|             ] | ||||
|             // "showDevDebugOutput": "raw", | ||||
| @ -91,27 +95,20 @@ | ||||
|             "type": "cortex-debug", | ||||
|             "servertype": "openocd", | ||||
|             "device": "cmsis-dap", | ||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", | ||||
|             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||
|             "rtos": "FreeRTOS", | ||||
|             "configFiles": [ | ||||
|                 "interface/cmsis-dap.cfg", | ||||
|                 "./debug/stm32wbx.cfg", | ||||
|                 "./scripts/debug/stm32wbx.cfg", | ||||
|             ], | ||||
|             "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", | ||||
|             ], | ||||
|             // "showDevDebugOutput": "raw", | ||||
|         }, | ||||
|         { | ||||
|             "name": "fbt debug", | ||||
|             "type": "python", | ||||
|             "request": "launch", | ||||
|             "program": "./lib/scons/scripts/scons.py", | ||||
|             "args": [ | ||||
|                 "plugin_dist" | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "python debug", | ||||
|             "type": "python", | ||||
|  | ||||
							
								
								
									
										5
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @ -8,11 +8,12 @@ | ||||
| 		"amiralizadeh9480.cpp-helper", | ||||
| 		"marus25.cortex-debug", | ||||
| 		"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. | ||||
| 	"unwantedRecommendations": [ | ||||
| 		"twxs.cmake", | ||||
| 		"ms-vscode.cmake-tools" | ||||
| 	] | ||||
| } | ||||
| } | ||||
| @ -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 | ||||
| - `assets`          - assets used by applications and services | ||||
| - `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 | ||||
| - `firmware`        - firmware source code | ||||
| - `lib`             - our and 3rd party libraries, drivers, etc. | ||||
|  | ||||
							
								
								
									
										22
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								SConstruct
									
									
									
									
									
								
							| @ -239,19 +239,31 @@ distenv.PhonyTarget( | ||||
| ) | ||||
| 
 | ||||
| # 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( | ||||
|     "debug_other", | ||||
|     "${GDBPYCOM}", | ||||
|     GDBOPTS="${GDBOPTS_BASE}", | ||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||
|     GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', | ||||
|     GDBPYOPTS=debug_other_opts, | ||||
| ) | ||||
| 
 | ||||
| distenv.PhonyTarget( | ||||
|     "debug_other_blackmagic", | ||||
|     "${GDBPYCOM}", | ||||
|     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.NoClean(vscode_dist) | ||||
| distenv.Alias("vscode_dist", vscode_dist) | ||||
| 
 | ||||
| # Configure shell with build tools | ||||
| distenv.PhonyTarget( | ||||
|     "env", | ||||
|     "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", | ||||
| ) | ||||
|  | ||||
| @ -407,7 +407,7 @@ MU_TEST(subghz_decoder_ido_test) { | ||||
|         "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(subghz_decoder_keelog_test) { | ||||
| MU_TEST(subghz_decoder_keeloq_test) { | ||||
|     mu_assert( | ||||
|         subghz_decoder_test( | ||||
|             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"); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(subghz_encoder_keelog_test) { | ||||
| MU_TEST(subghz_encoder_keeloq_test) { | ||||
|     mu_assert( | ||||
|         subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), | ||||
|         "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_hormann_hsm_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_nero_radio_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_gate_tx_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_delta3_test); | ||||
|     MU_RUN_TEST(subghz_encoder_megacode_test); | ||||
|  | ||||
							
								
								
									
										22
									
								
								applications/external/hid_app/hid.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								applications/external/hid_app/hid.c
									
									
									
									
										vendored
									
									
								
							| @ -11,6 +11,7 @@ enum HidDebugSubmenuIndex { | ||||
|     HidSubmenuIndexMedia, | ||||
|     HidSubmenuIndexTikTok, | ||||
|     HidSubmenuIndexMouse, | ||||
|     HidSubmenuIndexMouseClicker, | ||||
|     HidSubmenuIndexMouseJiggler, | ||||
| }; | ||||
| 
 | ||||
| @ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { | ||||
|     } else if(index == HidSubmenuIndexTikTok) { | ||||
|         app->view_id = 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) { | ||||
|         app->view_id = 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_media_set_connected_status(hid->hid_media, 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_tiktok_set_connected_status(hid->hid_tiktok, connected); | ||||
| } | ||||
| @ -114,6 +119,12 @@ Hid* hid_alloc(HidTransport transport) { | ||||
|             hid_submenu_callback, | ||||
|             app); | ||||
|     } | ||||
|     submenu_add_item( | ||||
|         app->device_type_submenu, | ||||
|         "Mouse Clicker", | ||||
|         HidSubmenuIndexMouseClicker, | ||||
|         hid_submenu_callback, | ||||
|         app); | ||||
|     submenu_add_item( | ||||
|         app->device_type_submenu, | ||||
|         "Mouse Jiggler", | ||||
| @ -172,6 +183,15 @@ Hid* hid_app_alloc_view(void* context) { | ||||
|     view_dispatcher_add_view( | ||||
|         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
 | ||||
|     app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); | ||||
|     view_set_previous_callback( | ||||
| @ -205,6 +225,8 @@ void hid_free(Hid* app) { | ||||
|     hid_media_free(app->hid_media); | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); | ||||
|     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); | ||||
|     hid_mouse_jiggler_free(app->hid_mouse_jiggler); | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); | ||||
|  | ||||
							
								
								
									
										2
									
								
								applications/external/hid_app/hid.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								applications/external/hid_app/hid.h
									
									
									
									
										vendored
									
									
								
							| @ -20,6 +20,7 @@ | ||||
| #include "views/hid_keyboard.h" | ||||
| #include "views/hid_media.h" | ||||
| #include "views/hid_mouse.h" | ||||
| #include "views/hid_mouse_clicker.h" | ||||
| #include "views/hid_mouse_jiggler.h" | ||||
| #include "views/hid_tiktok.h" | ||||
| 
 | ||||
| @ -43,6 +44,7 @@ struct Hid { | ||||
|     HidKeyboard* hid_keyboard; | ||||
|     HidMedia* hid_media; | ||||
|     HidMouse* hid_mouse; | ||||
|     HidMouseClicker* hid_mouse_clicker; | ||||
|     HidMouseJiggler* hid_mouse_jiggler; | ||||
|     HidTikTok* hid_tiktok; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								applications/external/hid_app/views.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								applications/external/hid_app/views.h
									
									
									
									
										vendored
									
									
								
							| @ -4,6 +4,7 @@ typedef enum { | ||||
|     HidViewKeyboard, | ||||
|     HidViewMedia, | ||||
|     HidViewMouse, | ||||
|     HidViewMouseClicker, | ||||
|     HidViewMouseJiggler, | ||||
|     BtHidViewTikTok, | ||||
|     HidViewExitConfirm, | ||||
|  | ||||
							
								
								
									
										214
									
								
								applications/external/hid_app/views/hid_mouse_clicker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								applications/external/hid_app/views/hid_mouse_clicker.c
									
									
									
									
										vendored
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										14
									
								
								applications/external/hid_app/views/hid_mouse_clicker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/external/hid_app/views/hid_mouse_clicker.h
									
									
									
									
										vendored
									
									
										Normal 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); | ||||
| @ -95,7 +95,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { | ||||
| 
 | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->key == InputKeyOk) { | ||||
|     if(event->type == InputTypeShort && event->key == InputKeyOk) { | ||||
|         with_view_model( | ||||
|             hid_mouse_jiggler->view, | ||||
|             HidMouseJigglerModel * model, | ||||
|  | ||||
							
								
								
									
										17
									
								
								applications/external/mfkey32/application.fam
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/external/mfkey32/application.fam
									
									
									
									
										vendored
									
									
										Normal 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", | ||||
| ) | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/mfkey32/images/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/external/mfkey32/images/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/mfkey32/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											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
									
								
							
							
						
						
									
										1349
									
								
								applications/external/mfkey32/mfkey32.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { | ||||
|     uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; | ||||
|     memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); | ||||
|     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); | ||||
|  | ||||
| @ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { | ||||
|     &ws_protocol_auriol_th, | ||||
|     &ws_protocol_oregon_v1, | ||||
|     &ws_protocol_tx_8300, | ||||
|     &ws_protocol_wendox_w6726, | ||||
| }; | ||||
| 
 | ||||
| const SubGhzProtocolRegistry weather_station_protocol_registry = { | ||||
|  | ||||
| @ -16,5 +16,6 @@ | ||||
| #include "auriol_hg0601a.h" | ||||
| #include "oregon_v1.h" | ||||
| #include "tx_8300.h" | ||||
| #include "wendox_w6726.h" | ||||
| 
 | ||||
| extern const SubGhzProtocolRegistry weather_station_protocol_registry; | ||||
|  | ||||
							
								
								
									
										299
									
								
								applications/external/weather_station/protocols/wendox_w6726.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								applications/external/weather_station/protocols/wendox_w6726.c
									
									
									
									
										vendored
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										80
									
								
								applications/external/weather_station/protocols/wendox_w6726.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								applications/external/weather_station/protocols/wendox_w6726.h
									
									
									
									
										vendored
									
									
										Normal 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); | ||||
| @ -12,7 +12,7 @@ | ||||
| #define MENU_ITEMS 4u | ||||
| #define UNLOCK_CNT 3 | ||||
| 
 | ||||
| #define SUBGHZ_RAW_TRESHOLD_MIN -90.0f | ||||
| #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f | ||||
| typedef struct { | ||||
|     FuriString* item_str; | ||||
|     uint8_t type; | ||||
| @ -69,10 +69,10 @@ void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { | ||||
|         instance->view, | ||||
|         WSReceiverModel * model, | ||||
|         { | ||||
|             if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { | ||||
|             if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||
|                 model->u_rssi = 0; | ||||
|             } else { | ||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); | ||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
|  | ||||
| @ -152,22 +152,22 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, | ||||
| } | ||||
| 
 | ||||
| static const DuckyCmd ducky_commands[] = { | ||||
|     {"REM ", NULL, -1}, | ||||
|     {"ID ", NULL, -1}, | ||||
|     {"DELAY ", ducky_fnc_delay, -1}, | ||||
|     {"STRING ", ducky_fnc_string, 0}, | ||||
|     {"STRINGLN ", ducky_fnc_string, 1}, | ||||
|     {"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, | ||||
|     {"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, | ||||
|     {"STRINGDELAY ", ducky_fnc_strdelay, -1}, | ||||
|     {"STRING_DELAY ", ducky_fnc_strdelay, -1}, | ||||
|     {"REPEAT ", ducky_fnc_repeat, -1}, | ||||
|     {"SYSRQ ", ducky_fnc_sysrq, -1}, | ||||
|     {"ALTCHAR ", ducky_fnc_altchar, -1}, | ||||
|     {"ALTSTRING ", ducky_fnc_altstring, -1}, | ||||
|     {"ALTCODE ", ducky_fnc_altstring, -1}, | ||||
|     {"HOLD ", ducky_fnc_hold, -1}, | ||||
|     {"RELEASE ", ducky_fnc_release, -1}, | ||||
|     {"REM", NULL, -1}, | ||||
|     {"ID", NULL, -1}, | ||||
|     {"DELAY", ducky_fnc_delay, -1}, | ||||
|     {"STRING", ducky_fnc_string, 0}, | ||||
|     {"STRINGLN", ducky_fnc_string, 1}, | ||||
|     {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, | ||||
|     {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, | ||||
|     {"STRINGDELAY", ducky_fnc_strdelay, -1}, | ||||
|     {"STRING_DELAY", ducky_fnc_strdelay, -1}, | ||||
|     {"REPEAT", ducky_fnc_repeat, -1}, | ||||
|     {"SYSRQ", ducky_fnc_sysrq, -1}, | ||||
|     {"ALTCHAR", ducky_fnc_altchar, -1}, | ||||
|     {"ALTSTRING", ducky_fnc_altstring, -1}, | ||||
|     {"ALTCODE", ducky_fnc_altstring, -1}, | ||||
|     {"HOLD", ducky_fnc_hold, -1}, | ||||
|     {"RELEASE", ducky_fnc_release, -1}, | ||||
|     {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, | ||||
| }; | ||||
| 
 | ||||
| @ -175,8 +175,15 @@ static const DuckyCmd ducky_commands[] = { | ||||
| #define WORKER_TAG TAG "Worker" | ||||
| 
 | ||||
| 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++) { | ||||
|         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) { | ||||
|                 return 0; | ||||
|             } else { | ||||
|  | ||||
| @ -18,7 +18,7 @@ void nfc_scene_mfkey_complete_on_enter(void* context) { | ||||
|         AlignCenter, | ||||
|         AlignCenter, | ||||
|         FontSecondary, | ||||
|         "Now use mfkey32v2\nto extract keys"); | ||||
|         "Now use Mfkey32\nto extract keys"); | ||||
|     widget_add_button_element( | ||||
|         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; | ||||
| 
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -6,14 +6,13 @@ typedef enum { | ||||
|     SubGhzCustomEventManagerSetRAW, | ||||
| 
 | ||||
|     //SubmenuIndex
 | ||||
|     SubmenuIndexPricenton, | ||||
|     SubmenuIndexPricenton_433, | ||||
|     SubmenuIndexPricenton_315, | ||||
|     SubmenuIndexNiceFlo12bit, | ||||
|     SubmenuIndexNiceFlo24bit, | ||||
|     SubmenuIndexCAME12bit, | ||||
|     SubmenuIndexCAME24bit, | ||||
|     SubmenuIndexCAMETwee, | ||||
|     SubmenuIndexNeroSketch, | ||||
|     SubmenuIndexNeroRadio, | ||||
|     SubmenuIndexGateTX, | ||||
|     SubmenuIndexDoorHan_315_00, | ||||
|     SubmenuIndexDoorHan_433_92, | ||||
|  | ||||
| @ -261,7 +261,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont | ||||
|     instance->thread = furi_thread_alloc_ex( | ||||
|         "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); | ||||
|     SubGhz* subghz = context; | ||||
|     instance->setting = subghz->setting; | ||||
|     instance->setting = subghz_txrx_get_setting(subghz->txrx); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										60
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										43
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.h
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										521
									
								
								applications/main/subghz/helpers/subghz_txrx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								applications/main/subghz/helpers/subghz_txrx.c
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										290
									
								
								applications/main/subghz/helpers/subghz_txrx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								applications/main/subghz/helpers/subghz_txrx.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
							
								
								
									
										27
									
								
								applications/main/subghz/helpers/subghz_txrx_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/main/subghz/helpers/subghz_txrx_i.h
									
									
									
									
									
										Normal 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; | ||||
| }; | ||||
| @ -77,3 +77,10 @@ typedef enum { | ||||
|     SubGhzViewIdTestCarrier, | ||||
|     SubGhzViewIdTestPacket, | ||||
| } SubGhzViewId; | ||||
| 
 | ||||
| /** SubGhz load type file */ | ||||
| typedef enum { | ||||
|     SubGhzLoadTypeFileNoLoad, | ||||
|     SubGhzLoadTypeFileKey, | ||||
|     SubGhzLoadTypeFileRaw, | ||||
| } SubGhzLoadTypeFile; | ||||
|  | ||||
| @ -19,7 +19,7 @@ void subghz_scene_delete_on_enter(void* context) { | ||||
|     modulation_str = 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( | ||||
|         subghz->widget, | ||||
|         78, | ||||
| @ -37,7 +37,7 @@ void subghz_scene_delete_on_enter(void* context) { | ||||
|         AlignTop, | ||||
|         FontSecondary, | ||||
|         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( | ||||
|         subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); | ||||
| 
 | ||||
|  | ||||
| @ -33,7 +33,7 @@ void subghz_scene_delete_raw_on_enter(void* context) { | ||||
| 
 | ||||
|     widget_add_string_element( | ||||
|         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( | ||||
|         subghz->widget, | ||||
|         35, | ||||
|  | ||||
| @ -37,27 +37,23 @@ void subghz_scene_need_saving_on_enter(void* context) { | ||||
| bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||
|         scene_manager_previous_scene(subghz->scene_manager); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubGhzCustomEventSceneStay) { | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||
|             scene_manager_previous_scene(subghz->scene_manager); | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventSceneExit) { | ||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|                 subghz_preset_init( | ||||
|                     subghz, | ||||
|                     "AM650", | ||||
|                     subghz_setting_get_default_frequency(subghz->setting), | ||||
|                     NULL, | ||||
|                     0); | ||||
|             SubGhzRxKeyState state = subghz_rx_key_state_get(subghz); | ||||
|             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||
| 
 | ||||
|             if(state == SubGhzRxKeyStateExit) { | ||||
|                 subghz_set_default_preset(subghz); | ||||
|                 scene_manager_search_and_switch_to_previous_scene( | ||||
|                     subghz->scene_manager, SubGhzSceneStart); | ||||
|             } else { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|                 scene_manager_previous_scene(subghz->scene_manager); | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -3,11 +3,9 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <lib/subghz/protocols/raw.h> | ||||
| #include <lib/toolbox/path.h> | ||||
| #include <float_tools.h> | ||||
| 
 | ||||
| #define RAW_FILE_NAME "Raw_signal_" | ||||
| #define TAG "SubGhzSceneReadRAW" | ||||
| #define RAW_THRESHOLD_RSSI_LOW_COUNT 10 | ||||
| 
 | ||||
| bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | ||||
|     bool ret = false; | ||||
| @ -15,12 +13,13 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | ||||
|     FuriString* temp_str; | ||||
|     temp_str = furi_string_alloc(); | ||||
|     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"); | ||||
|             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"); | ||||
|             break; | ||||
|         } | ||||
| @ -38,13 +37,10 @@ static void subghz_scene_read_raw_update_statusbar(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     FuriString* frequency_str; | ||||
|     FuriString* modulation_str; | ||||
|     FuriString* frequency_str = furi_string_alloc(); | ||||
|     FuriString* modulation_str = furi_string_alloc(); | ||||
| 
 | ||||
|     frequency_str = furi_string_alloc(); | ||||
|     modulation_str = furi_string_alloc(); | ||||
| 
 | ||||
|     subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); | ||||
|     subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||
|     subghz_read_raw_add_data_statusbar( | ||||
|         subghz->subghz_read_raw, | ||||
|         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) { | ||||
|     SubGhz* subghz = context; | ||||
|     FuriString* file_name; | ||||
|     file_name = furi_string_alloc(); | ||||
|     FuriString* 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: | ||||
|         subghz_read_raw_set_status( | ||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); | ||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi); | ||||
|         break; | ||||
|     case SubGhzRxKeyStateRAWLoad: | ||||
|         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, | ||||
|             SubGhzReadRAWStatusLoadKeyTX, | ||||
|             furi_string_get_cstr(file_name), | ||||
|             subghz->txrx->raw_threshold_rssi); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|             threshold_rssi); | ||||
|         break; | ||||
|     case SubGhzRxKeyStateRAWSave: | ||||
|         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, | ||||
|             SubGhzReadRAWStatusSaveKey, | ||||
|             furi_string_get_cstr(file_name), | ||||
|             subghz->txrx->raw_threshold_rssi); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|             threshold_rssi); | ||||
|         break; | ||||
|     default: | ||||
|         subghz_read_raw_set_status( | ||||
|             subghz->subghz_read_raw, | ||||
|             SubGhzReadRAWStatusStart, | ||||
|             "", | ||||
|             subghz->txrx->raw_threshold_rssi); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "", threshold_rssi); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) { | ||||
|         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||
|     } | ||||
|     furi_string_free(file_name); | ||||
|     subghz_scene_read_raw_update_statusbar(subghz); | ||||
| 
 | ||||
|     //set callback view raw
 | ||||
|     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( | ||||
|         subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME); | ||||
|     furi_assert(subghz->txrx->decoder_result); | ||||
|     furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME)); | ||||
| 
 | ||||
|     //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); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     bool consumed = false; | ||||
|     SubGhzProtocolDecoderRAW* decoder_raw = | ||||
|         (SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx); | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case SubGhzCustomEventViewReadRAWBack: | ||||
|             //Stop TX
 | ||||
|             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_txrx_stop(subghz->txrx); | ||||
|             //Stop save file
 | ||||
|             subghz_protocol_raw_save_to_file_stop( | ||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); | ||||
|             subghz_protocol_raw_save_to_file_stop(decoder_raw); | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             //needed save?
 | ||||
|             if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || | ||||
|                (subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; | ||||
|             if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || | ||||
|                (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { | ||||
|                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||
|             } else { | ||||
|                 //Restore default setting
 | ||||
|                 subghz_preset_init( | ||||
|                     subghz, | ||||
|                     "AM650", | ||||
|                     subghz_setting_get_default_frequency(subghz->setting), | ||||
|                     NULL, | ||||
|                     0); | ||||
|                 subghz_set_default_preset(subghz); | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneSaved)) { | ||||
|                     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; | ||||
| 
 | ||||
|         case SubGhzCustomEventViewReadRAWTXRXStop: | ||||
|             //Stop TX
 | ||||
|             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_txrx_stop(subghz->txrx); | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             consumed = true; | ||||
|             break; | ||||
| @ -187,13 +158,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             break; | ||||
| 
 | ||||
|         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)) { | ||||
|                     furi_string_set(subghz->file_path_tmp, subghz->file_path); | ||||
|                     subghz_delete_file(subghz); | ||||
|                 } | ||||
|             } | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||
|             notification_message(subghz->notifications, &sequence_reset_rgb); | ||||
|             consumed = true; | ||||
|             break; | ||||
| @ -203,7 +174,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                 if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                     scene_manager_set_scene_state( | ||||
|                         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); | ||||
|                     consumed = true; | ||||
|                 } 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)) { | ||||
|                 //start send
 | ||||
|                 subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|                 if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                     subghz_rx_end(subghz); | ||||
|                 } | ||||
|                 if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || | ||||
|                    (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { | ||||
|                     if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { | ||||
|                         subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|                         subghz_read_raw_set_status( | ||||
|                             subghz->subghz_read_raw, | ||||
|                             SubGhzReadRAWStatusIDLE, | ||||
|                             "", | ||||
|                             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; | ||||
|                 if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { | ||||
|                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||
|                     subghz_read_raw_set_status( | ||||
|                         subghz->subghz_read_raw, | ||||
|                         SubGhzReadRAWStatusIDLE, | ||||
|                         "", | ||||
|                         subghz_threshold_rssi_get(subghz->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_txrx_set_raw_file_encoder_worker_callback_end( | ||||
|                         subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz); | ||||
|                     subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|                 } | ||||
|             } else { | ||||
|                 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: | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_speaker_unmute(subghz); | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             } | ||||
|             subghz_txrx_stop(subghz->txrx); | ||||
|             subghz_read_raw_stop_send(subghz->subghz_read_raw); | ||||
|             consumed = true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubGhzCustomEventViewReadRAWIDLE: | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             }; | ||||
|             subghz_txrx_stop(subghz->txrx); | ||||
|             size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw); | ||||
| 
 | ||||
|             size_t spl_count = subghz_protocol_raw_get_sample_write( | ||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); | ||||
|             subghz_protocol_raw_save_to_file_stop(decoder_raw); | ||||
| 
 | ||||
|             subghz_protocol_raw_save_to_file_stop( | ||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); | ||||
| 
 | ||||
|             FuriString* temp_str; | ||||
|             temp_str = furi_string_alloc(); | ||||
|             FuriString* temp_str = furi_string_alloc(); | ||||
|             furi_string_printf( | ||||
|                 temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); | ||||
|             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); | ||||
| 
 | ||||
|             if(spl_count > 0) { | ||||
| @ -299,32 +248,21 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             } | ||||
| 
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; | ||||
|             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||
| 
 | ||||
|             consumed = true; | ||||
|             break; | ||||
| 
 | ||||
|         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); | ||||
|             } else { | ||||
|                 subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; | ||||
|                 if(subghz_protocol_raw_save_to_file_init( | ||||
|                        (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, | ||||
|                        RAW_FILE_NAME, | ||||
|                        subghz->txrx->preset)) { | ||||
|                 SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||
|                 if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzRawRec); | ||||
|                     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_txrx_rx_start(subghz->txrx); | ||||
|                     subghz->state_notifications = SubGhzNotificationStateRx; | ||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; | ||||
|                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||
|                 } else { | ||||
|                     furi_string_set(subghz->error_str, "Function requires\nan SD card."); | ||||
|                     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)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     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); | ||||
|             } else { | ||||
|                 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) { | ||||
|         case SubGhzNotificationStateRx: | ||||
|             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||
| 
 | ||||
|             subghz_read_raw_update_sample_write( | ||||
|                 subghz->subghz_read_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); | ||||
|                 } | ||||
|             } | ||||
|                 subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw)); | ||||
| 
 | ||||
|             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; | ||||
|         case SubGhzNotificationStateTx: | ||||
|             notification_message(subghz->notifications, &sequence_blink_magenta_10); | ||||
| @ -407,13 +319,10 @@ void subghz_scene_read_raw_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     //Stop CC1101
 | ||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|         subghz_rx_end(subghz); | ||||
|         subghz_sleep(subghz); | ||||
|     }; | ||||
|     subghz_txrx_stop(subghz->txrx); | ||||
|     subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||
| 
 | ||||
|     //filter restoration
 | ||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); | ||||
|     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||
| } | ||||
|  | ||||
| @ -35,16 +35,12 @@ static const NotificationSequence subghs_sequence_rx_locked = { | ||||
| 
 | ||||
| static void subghz_scene_receiver_update_statusbar(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     FuriString* history_stat_str; | ||||
|     history_stat_str = furi_string_alloc(); | ||||
|     if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { | ||||
|         FuriString* frequency_str; | ||||
|         FuriString* modulation_str; | ||||
|     FuriString* history_stat_str = furi_string_alloc(); | ||||
|     if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) { | ||||
|         FuriString* frequency_str = furi_string_alloc(); | ||||
|         FuriString* modulation_str = furi_string_alloc(); | ||||
| 
 | ||||
|         frequency_str = furi_string_alloc(); | ||||
|         modulation_str = furi_string_alloc(); | ||||
| 
 | ||||
|         subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); | ||||
|         subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||
| 
 | ||||
|         subghz_view_receiver_add_data_statusbar( | ||||
|             subghz->subghz_receiver, | ||||
| @ -74,80 +70,68 @@ static void subghz_scene_add_to_history_callback( | ||||
|     void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     FuriString* str_buff; | ||||
|     str_buff = furi_string_alloc(); | ||||
|     SubGhzHistory* history = subghz->history; | ||||
|     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); | ||||
| 
 | ||||
|         subghz->state_notifications = SubGhzNotificationStateRxDone; | ||||
| 
 | ||||
|         subghz_history_get_text_item_menu( | ||||
|             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); | ||||
|         uint16_t item_history = subghz_history_get_item(history); | ||||
|         subghz_history_get_text_item_menu(history, str_buff, item_history - 1); | ||||
|         subghz_view_receiver_add_item_to_menu( | ||||
|             subghz->subghz_receiver, | ||||
|             furi_string_get_cstr(str_buff), | ||||
|             subghz_history_get_type_protocol( | ||||
|                 subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); | ||||
|             subghz_history_get_type_protocol(history, item_history - 1)); | ||||
| 
 | ||||
|         subghz_scene_receiver_update_statusbar(subghz); | ||||
|     } | ||||
|     subghz_receiver_reset(receiver); | ||||
|     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) { | ||||
|     SubGhz* subghz = context; | ||||
|     SubGhzHistory* history = subghz->history; | ||||
| 
 | ||||
|     FuriString* str_buff; | ||||
|     str_buff = furi_string_alloc(); | ||||
| 
 | ||||
|     if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { | ||||
|         subghz_preset_init( | ||||
|             subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); | ||||
|         subghz_history_reset(subghz->txrx->history); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; | ||||
|     if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) { | ||||
|         subghz_set_default_preset(subghz); | ||||
|         subghz_history_reset(history); | ||||
|         subghz_rx_key_state_set(subghz, 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
 | ||||
|     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); | ||||
|         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->subghz_receiver, | ||||
|             furi_string_get_cstr(str_buff), | ||||
|             subghz_history_get_type_protocol(subghz->txrx->history, i)); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; | ||||
|             subghz_history_get_type_protocol(history, i)); | ||||
|         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||
|     } | ||||
|     furi_string_free(str_buff); | ||||
|     subghz_scene_receiver_update_statusbar(subghz); | ||||
|     subghz_view_receiver_set_callback( | ||||
|         subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||
|     subghz_receiver_set_rx_callback( | ||||
|         subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); | ||||
|     subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); | ||||
| 
 | ||||
|     subghz->state_notifications = SubGhzNotificationStateRx; | ||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|         subghz_rx_end(subghz); | ||||
|     }; | ||||
|     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); | ||||
|     subghz_txrx_rx_start(subghz->txrx); | ||||
|     subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); | ||||
| 
 | ||||
|     //to use a universal decoder, we are looking for a link to it
 | ||||
|     subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( | ||||
|         subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); | ||||
|     furi_assert(subghz->txrx->decoder_result); | ||||
|     furi_check( | ||||
|         subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); | ||||
| 
 | ||||
|     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: | ||||
|             // Stop CC1101 Rx
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             }; | ||||
|             subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||
|             subghz->txrx->idx_menu_chosen = 0; | ||||
|             subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); | ||||
|             subghz_txrx_stop(subghz->txrx); | ||||
|             subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); | ||||
|             subghz->idx_menu_chosen = 0; | ||||
|             subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); | ||||
| 
 | ||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; | ||||
|             if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { | ||||
|                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||
|             } else { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|                 subghz_preset_init( | ||||
|                     subghz, | ||||
|                     "AM650", | ||||
|                     subghz_setting_get_default_frequency(subghz->setting), | ||||
|                     NULL, | ||||
|                     0); | ||||
|                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||
|                 subghz_set_default_preset(subghz); | ||||
|                 scene_manager_search_and_switch_to_previous_scene( | ||||
|                     subghz->scene_manager, SubGhzSceneStart); | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case SubGhzCustomEventViewReceiverOK: | ||||
|             subghz->txrx->idx_menu_chosen = | ||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||
|             DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case SubGhzCustomEventViewReceiverConfig: | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             subghz->txrx->idx_menu_chosen = | ||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); | ||||
|             consumed = true; | ||||
|             break; | ||||
| @ -203,30 +177,30 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case SubGhzCustomEventViewReceiverUnlock: | ||||
|             subghz->lock = SubGhzLockOff; | ||||
|             subghz_unlock(subghz); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||
|             subghz_hopper_update(subghz); | ||||
|         if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { | ||||
|             subghz_txrx_hopper_update(subghz->txrx); | ||||
|             subghz_scene_receiver_update_statusbar(subghz); | ||||
|         } | ||||
| 
 | ||||
|         //get RSSI
 | ||||
|         float rssi = furi_hal_subghz_get_rssi(); | ||||
|         subghz_receiver_rssi(subghz->subghz_receiver, rssi); | ||||
|         SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi); | ||||
| 
 | ||||
|         subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.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) { | ||||
|         case SubGhzNotificationStateRx: | ||||
|             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||
|             break; | ||||
|         case SubGhzNotificationStateRxDone: | ||||
|             if(subghz->lock != SubGhzLockOn) { | ||||
|             if(!subghz_is_locked(subghz)) { | ||||
|                 notification_message(subghz->notifications, &subghs_sequence_rx); | ||||
|             } else { | ||||
|                 notification_message(subghz->notifications, &subghs_sequence_rx_locked); | ||||
|  | ||||
| @ -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) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||
| 
 | ||||
|     uint8_t index = 0; | ||||
|     for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { | ||||
|         if(value == subghz_setting_get_frequency(subghz->setting, i)) { | ||||
|     for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) { | ||||
|         if(value == subghz_setting_get_frequency(setting, i)) { | ||||
|             index = i; | ||||
|             break; | ||||
|         } else { | ||||
|             index = subghz_setting_get_frequency_default_index(subghz->setting); | ||||
|             index = subghz_setting_get_frequency_default_index(setting); | ||||
|         } | ||||
|     } | ||||
|     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) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||
| 
 | ||||
|     uint8_t index = 0; | ||||
|     for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { | ||||
|         if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { | ||||
|     for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) { | ||||
|         if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) { | ||||
|             index = i; | ||||
|             break; | ||||
|         } 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; | ||||
| @ -122,70 +126,84 @@ uint8_t subghz_scene_receiver_config_hopper_value_index( | ||||
| static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { | ||||
|     SubGhz* subghz = variable_item_get_context(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}; | ||||
|         uint32_t frequency = subghz_setting_get_frequency(setting, index); | ||||
|         SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||
| 
 | ||||
|         snprintf( | ||||
|             text_buf, | ||||
|             sizeof(text_buf), | ||||
|             "%lu.%02lu", | ||||
|             subghz_setting_get_frequency(subghz->setting, index) / 1000000, | ||||
|             (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); | ||||
|             frequency / 1000000, | ||||
|             (frequency % 1000000) / 10000); | ||||
|         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 { | ||||
|         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) { | ||||
|     SubGhz* subghz = variable_item_get_context(item); | ||||
|     uint8_t index = variable_item_get_current_value_index(item); | ||||
|     variable_item_set_current_value_text( | ||||
|         item, subghz_setting_get_preset_name(subghz->setting, index)); | ||||
|     subghz_preset_init( | ||||
|         subghz, | ||||
|         subghz_setting_get_preset_name(subghz->setting, index), | ||||
|         subghz->txrx->preset->frequency, | ||||
|         subghz_setting_get_preset_data(subghz->setting, index), | ||||
|         subghz_setting_get_preset_data_size(subghz->setting, index)); | ||||
|     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||
| 
 | ||||
|     variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index)); | ||||
| 
 | ||||
|     SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||
|     subghz_txrx_set_preset( | ||||
|         subghz->txrx, | ||||
|         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) { | ||||
|     SubGhz* subghz = variable_item_get_context(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]); | ||||
|     if(hopping_value[index] == SubGhzHopperStateOFF) { | ||||
|         char text_buf[10] = {0}; | ||||
|         uint32_t frequency = subghz_setting_get_default_frequency(setting); | ||||
|         SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||
| 
 | ||||
|         snprintf( | ||||
|             text_buf, | ||||
|             sizeof(text_buf), | ||||
|             "%lu.%02lu", | ||||
|             subghz_setting_get_default_frequency(subghz->setting) / 1000000, | ||||
|             (subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); | ||||
|         variable_item_set_current_value_text( | ||||
|             (VariableItem*)scene_manager_get_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||
|             text_buf); | ||||
|         subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); | ||||
|             frequency / 1000000, | ||||
|             (frequency % 1000000) / 10000); | ||||
|         variable_item_set_current_value_text(frequency_item, text_buf); | ||||
| 
 | ||||
|         subghz_txrx_set_preset( | ||||
|             subghz->txrx, | ||||
|             furi_string_get_cstr(preset.name), | ||||
|             frequency, | ||||
|             preset.data, | ||||
|             preset.data_size); | ||||
|         variable_item_set_current_value_index( | ||||
|             (VariableItem*)scene_manager_get_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||
|             subghz_setting_get_frequency_default_index(subghz->setting)); | ||||
|             frequency_item, subghz_setting_get_frequency_default_index(setting)); | ||||
|     } else { | ||||
|         variable_item_set_current_value_text( | ||||
|             (VariableItem*)scene_manager_get_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||
|             " -----"); | ||||
|         variable_item_set_current_value_text(frequency_item, " -----"); | ||||
|         variable_item_set_current_value_index( | ||||
|             (VariableItem*)scene_manager_get_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||
|             subghz_setting_get_frequency_default_index(subghz->setting)); | ||||
|             frequency_item, subghz_setting_get_frequency_default_index(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) { | ||||
| @ -193,7 +211,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { | ||||
|     uint8_t index = variable_item_get_current_value_index(item); | ||||
| 
 | ||||
|     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) { | ||||
| @ -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); | ||||
| 
 | ||||
|     variable_item_set_current_value_text(item, bin_raw_text[index]); | ||||
|     subghz->txrx->filter = bin_raw_value[index]; | ||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); | ||||
|     subghz->filter = bin_raw_value[index]; | ||||
|     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|     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) { | ||||
| @ -226,25 +244,27 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     VariableItem* item; | ||||
|     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( | ||||
|         subghz->variable_item_list, | ||||
|         "Frequency:", | ||||
|         subghz_setting_get_frequency_count(subghz->setting), | ||||
|         subghz_setting_get_frequency_count(setting), | ||||
|         subghz_scene_receiver_config_set_frequency, | ||||
|         subghz); | ||||
|     value_index = | ||||
|         subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz); | ||||
|     value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); | ||||
|     scene_manager_set_scene_state( | ||||
|         subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); | ||||
|     variable_item_set_current_value_index(item, value_index); | ||||
|     char text_buf[10] = {0}; | ||||
|     uint32_t frequency = subghz_setting_get_frequency(setting, value_index); | ||||
|     snprintf( | ||||
|         text_buf, | ||||
|         sizeof(text_buf), | ||||
|         "%lu.%02lu", | ||||
|         subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, | ||||
|         (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); | ||||
|         frequency / 1000000, | ||||
|         (frequency % 1000000) / 10000); | ||||
|     variable_item_set_current_value_text(item, text_buf); | ||||
| 
 | ||||
|     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); | ||||
|         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_text(item, hopping_text[value_index]); | ||||
|     } | ||||
| @ -264,14 +284,14 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|     item = variable_item_list_add( | ||||
|         subghz->variable_item_list, | ||||
|         "Modulation:", | ||||
|         subghz_setting_get_preset_count(subghz->setting), | ||||
|         subghz_setting_get_preset_count(setting), | ||||
|         subghz_scene_receiver_config_set_preset, | ||||
|         subghz); | ||||
|     value_index = subghz_scene_receiver_config_next_preset( | ||||
|         furi_string_get_cstr(subghz->txrx->preset->name), subghz); | ||||
|     value_index = | ||||
|         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_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) != | ||||
|        SubGhzCustomEventManagerSet) { | ||||
| @ -281,7 +301,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|             BIN_RAW_COUNT, | ||||
|             subghz_scene_receiver_config_set_bin_raw, | ||||
|             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_text(item, bin_raw_text[value_index]); | ||||
|     } | ||||
| @ -292,7 +312,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|         SPEAKER_COUNT, | ||||
|         subghz_scene_receiver_config_set_speaker, | ||||
|         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_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); | ||||
|         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_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.event == SubGhzCustomEventSceneSettingLock) { | ||||
|             subghz->lock = SubGhzLockOn; | ||||
|             subghz_lock(subghz); | ||||
|             scene_manager_previous_scene(subghz->scene_manager); | ||||
|             consumed = true; | ||||
|         } | ||||
|  | ||||
| @ -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) { | ||||
|     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
 | ||||
|         subghz_protocol_decoder_base_deserialize( | ||||
|             subghz->txrx->decoder_result, | ||||
|             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||
|             subghz_txrx_get_decoder(subghz->txrx), | ||||
|             subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen)); | ||||
| 
 | ||||
|         SubGhzRadioPreset* preset = | ||||
|             subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||
|         subghz_preset_init( | ||||
|             subghz, | ||||
|             subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen); | ||||
|         subghz_txrx_set_preset( | ||||
|             subghz->txrx, | ||||
|             furi_string_get_cstr(preset->name), | ||||
|             preset->frequency, | ||||
|             preset->data, | ||||
| @ -47,15 +46,11 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(subghz_scene_receiver_info_update_parser(subghz)) { | ||||
|         FuriString* frequency_str; | ||||
|         FuriString* modulation_str; | ||||
|         FuriString* text; | ||||
|         FuriString* frequency_str = furi_string_alloc(); | ||||
|         FuriString* modulation_str = furi_string_alloc(); | ||||
|         FuriString* text = furi_string_alloc(); | ||||
| 
 | ||||
|         frequency_str = furi_string_alloc(); | ||||
|         modulation_str = 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( | ||||
|             subghz->widget, | ||||
|             78, | ||||
| @ -73,7 +68,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             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( | ||||
|             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(text); | ||||
| 
 | ||||
|         if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == | ||||
|            SubGhzProtocolFlag_Save) { | ||||
|         if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { | ||||
|             widget_add_button_element( | ||||
|                 subghz->widget, | ||||
|                 GuiButtonTypeRight, | ||||
| @ -90,10 +84,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|                 subghz_scene_receiver_info_callback, | ||||
|                 subghz); | ||||
|         } | ||||
|         if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == | ||||
|             SubGhzProtocolFlag_Send) && | ||||
|            subghz->txrx->decoder_result->protocol->encoder->deserialize && | ||||
|            subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) { | ||||
|         if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) { | ||||
|             widget_add_button_element( | ||||
|                 subghz->widget, | ||||
|                 GuiButtonTypeCenter, | ||||
| @ -114,82 +105,49 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         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)) { | ||||
|                 return false; | ||||
|             } | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || | ||||
|                subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||
|                 if(!subghz_tx_start( | ||||
|                        subghz, | ||||
|                        subghz_history_get_raw_data( | ||||
|                            subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { | ||||
|                     if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                         subghz_tx_stop(subghz); | ||||
|                     } | ||||
|                     if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { | ||||
|                         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; | ||||
|                 } | ||||
|             //CC1101 Stop RX -> Start TX
 | ||||
|             subghz_txrx_hopper_pause(subghz->txrx); | ||||
|             if(!subghz_tx_start( | ||||
|                    subghz, | ||||
|                    subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) { | ||||
|                 subghz_txrx_rx_start(subghz->txrx); | ||||
|                 subghz_txrx_hopper_unpause(subghz->txrx); | ||||
|                 subghz->state_notifications = SubGhzNotificationStateRx; | ||||
|             } else { | ||||
|                 subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|             } | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { | ||||
|             //CC1101 Stop Tx -> Start RX
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
|             } | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { | ||||
|                 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_txrx_rx_start(subghz->txrx); | ||||
| 
 | ||||
|             subghz_txrx_hopper_unpause(subghz->txrx); | ||||
|             subghz->state_notifications = SubGhzNotificationStateRx; | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { | ||||
|             //CC1101 Stop RX -> Save
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             } | ||||
|             subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); | ||||
| 
 | ||||
|             subghz_txrx_stop(subghz->txrx); | ||||
|             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == | ||||
|                SubGhzProtocolFlag_Save) { | ||||
|             if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { | ||||
|                 subghz_file_name_clear(subghz); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||
|             subghz_hopper_update(subghz); | ||||
|         if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { | ||||
|             subghz_txrx_hopper_update(subghz->txrx); | ||||
|         } | ||||
|         switch(subghz->state_notifications) { | ||||
|         case SubGhzNotificationStateTx: | ||||
|  | ||||
| @ -5,8 +5,7 @@ | ||||
| void subghz_scene_region_info_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     const FuriHalRegion* const region = furi_hal_region_get(); | ||||
|     FuriString* buffer; | ||||
|     buffer = furi_string_alloc(); | ||||
|     FuriString* buffer = furi_string_alloc(); | ||||
|     if(region) { | ||||
|         furi_string_cat_printf(buffer, "Region: %s,  bands:\n", region->country_code); | ||||
|         for(uint16_t i = 0; i < region->bands_count; ++i) { | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| typedef enum { | ||||
|     SubGhzRpcStateIdle, | ||||
|     SubGhzRpcStateLoaded, | ||||
|     SubGhzRpcStateTx, | ||||
| } SubGhzRpcState; | ||||
| 
 | ||||
| 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); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { | ||||
|             bool result = false; | ||||
|             if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && | ||||
|                (state == SubGhzRpcStateLoaded)) { | ||||
|                 result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||
|             if((state == SubGhzRpcStateLoaded)) { | ||||
|                 result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); | ||||
|                 state = SubGhzRpcStateTx; | ||||
|                 if(result) subghz_blink_start(subghz); | ||||
|             } | ||||
|             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); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { | ||||
|             bool result = false; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|             if(state == SubGhzRpcStateTx) { | ||||
|                 subghz_txrx_stop(subghz->txrx); | ||||
|                 subghz_blink_stop(subghz); | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|                 state = SubGhzRpcStateIdle; | ||||
|                 result = true; | ||||
|             } | ||||
|             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) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|         subghz_tx_stop(subghz); | ||||
|         subghz_sleep(subghz); | ||||
|     SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); | ||||
|     if(state != SubGhzRpcStateIdle) { | ||||
|         subghz_txrx_stop(subghz->txrx); | ||||
|         subghz_blink_stop(subghz); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -35,10 +35,8 @@ void subghz_scene_save_name_on_enter(void* context) { | ||||
|     TextInput* text_input = subghz->text_input; | ||||
|     bool dev_name_empty = false; | ||||
| 
 | ||||
|     FuriString* file_name; | ||||
|     FuriString* dir_name; | ||||
|     file_name = furi_string_alloc(); | ||||
|     dir_name = furi_string_alloc(); | ||||
|     FuriString* file_name = furi_string_alloc(); | ||||
|     FuriString* dir_name = furi_string_alloc(); | ||||
| 
 | ||||
|     if(!subghz_path_is_file(subghz->file_path)) { | ||||
|         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, | ||||
|         subghz->file_name_tmp, | ||||
|         MAX_TEXT_INPUT_LEN, // buffer size
 | ||||
|         MAX_TEXT_INPUT_LEN, | ||||
|         dev_name_empty); | ||||
| 
 | ||||
|     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) { | ||||
|                         subghz_save_protocol_to_file( | ||||
|                             subghz, | ||||
|                             subghz->txrx->fff_data, | ||||
|                             subghz_txrx_get_fff_data(subghz->txrx), | ||||
|                             furi_string_get_cstr(subghz->file_path)); | ||||
|                         scene_manager_set_scene_state( | ||||
|                             subghz->scene_manager, | ||||
| @ -115,8 +113,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|                     } else { | ||||
|                         subghz_save_protocol_to_file( | ||||
|                             subghz, | ||||
|                             subghz_history_get_raw_data( | ||||
|                                 subghz->txrx->history, subghz->txrx->idx_menu_chosen), | ||||
|                             subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen), | ||||
|                             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) != | ||||
|                    SubGhzCustomEventManagerNoSet) { | ||||
|                     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( | ||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||
|                 } else { | ||||
|  | ||||
| @ -26,10 +26,10 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) | ||||
|         if(event.event == SubGhzCustomEventSceneSaveSuccess) { | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    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( | ||||
|                        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( | ||||
|                            subghz->scene_manager, SubGhzSceneSaved)) { | ||||
|                         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); | ||||
|  | ||||
| @ -4,8 +4,8 @@ void subghz_scene_saved_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(subghz_load_protocol_from_file(subghz)) { | ||||
|         if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; | ||||
|         if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { | ||||
|             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||
|         } else { | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); | ||||
|  | ||||
| @ -1,63 +1,10 @@ | ||||
| #include "../subghz_i.h" | ||||
| #include <lib/subghz/protocols/keeloq.h> | ||||
| #include <lib/subghz/protocols/secplus_v1.h> | ||||
| #include <lib/subghz/protocols/secplus_v2.h> | ||||
| #include "../helpers/subghz_txrx_create_protocol_key.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> | ||||
| 
 | ||||
| #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) { | ||||
|     SubGhz* subghz = context; | ||||
|     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( | ||||
|         subghz->submenu, | ||||
|         "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); | ||||
|     submenu_add_item( | ||||
| @ -108,10 +61,6 @@ void subghz_scene_set_type_on_enter(void* context) { | ||||
|         SubmenuIndexCAMETwee, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         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( | ||||
|         subghz->submenu, | ||||
|         "Gate TX_433", | ||||
| @ -172,94 +121,59 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | ||||
|     bool generated_protocol = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         //ToDo Fix
 | ||||
|         uint32_t key = subghz_random_serial(); | ||||
|         uint32_t key = (uint32_t)rand(); | ||||
|         switch(event.event) { | ||||
|         case SubmenuIndexPricenton: | ||||
|         case SubmenuIndexPricenton_433: | ||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { | ||||
|                 uint32_t te = 400; | ||||
|                 flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol_and_te( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400); | ||||
|             break; | ||||
|         case SubmenuIndexPricenton_315: | ||||
|             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; | ||||
|         case SubmenuIndexNiceFlo12bit: | ||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12); | ||||
|             break; | ||||
|         case SubmenuIndexNiceFlo24bit: | ||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24); | ||||
|             break; | ||||
|         case SubmenuIndexCAME12bit: | ||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12); | ||||
|             break; | ||||
|         case SubmenuIndexCAME24bit: | ||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24); | ||||
|             break; | ||||
|         case SubmenuIndexLinear_300_00: | ||||
|             key = (key & 0x3FF); | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10); | ||||
|             break; | ||||
|         case SubmenuIndexCAMETwee: | ||||
|             key = (key & 0x0FFFFFF0); | ||||
|             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 = true; | ||||
|             } | ||||
| 
 | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54); | ||||
|             break; | ||||
|         // case SubmenuIndexNeroSketch:
 | ||||
|         //     /* code */
 | ||||
|         //     break;
 | ||||
|         // case SubmenuIndexNeroRadio:
 | ||||
|         //     /* code */
 | ||||
|         //     break;
 | ||||
|         case SubmenuIndexGateTX: | ||||
|             key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
 | ||||
|             uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); | ||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||
|                    subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { | ||||
|                 generated_protocol = true; | ||||
|             } | ||||
|             generated_protocol = subghz_txrx_gen_data_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24); | ||||
|             break; | ||||
|         case SubmenuIndexDoorHan_433_92: | ||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||
|             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); | ||||
|             generated_protocol = subghz_txrx_gen_keeloq_protocol( | ||||
|                 subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003); | ||||
|             if(!generated_protocol) { | ||||
|                 furi_string_set( | ||||
|                     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; | ||||
|         case SubmenuIndexDoorHan_315_00: | ||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||
|             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); | ||||
|             generated_protocol = subghz_txrx_gen_keeloq_protocol( | ||||
|                 subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003); | ||||
|             if(!generated_protocol) { | ||||
|                 furi_string_set( | ||||
|                     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; | ||||
|         case SubmenuIndexLiftMaster_315_00: | ||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { | ||||
|                 key = subghz_random_serial(); | ||||
|             } | ||||
|             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; | ||||
|             } | ||||
|             generated_protocol = | ||||
|                 subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 315000000); | ||||
|             break; | ||||
|         case SubmenuIndexLiftMaster_390_00: | ||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { | ||||
|                 key = subghz_random_serial(); | ||||
|             } | ||||
|             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; | ||||
|             } | ||||
|             generated_protocol = | ||||
|                 subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 390000000); | ||||
|             break; | ||||
|         case SubmenuIndexSecPlus_v2_310_00: | ||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||
|             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); | ||||
|             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||
|                 subghz->txrx, "AM650", 310000000, key, 0x68, 0xE500000); | ||||
|             break; | ||||
|         case SubmenuIndexSecPlus_v2_315_00: | ||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||
|             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); | ||||
|             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||
|                 subghz->txrx, "AM650", 315000000, key, 0x68, 0xE500000); | ||||
|             break; | ||||
|         case SubmenuIndexSecPlus_v2_390_00: | ||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||
|             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); | ||||
|             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||
|                 subghz->txrx, "AM650", 390000000, key, 0x68, 0xE500000); | ||||
|             break; | ||||
|         default: | ||||
|             return false; | ||||
|  | ||||
| @ -50,9 +50,10 @@ void subghz_scene_show_error_on_enter(void* context) { | ||||
| 
 | ||||
| bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     SubGhzCustomEvent scene_state = | ||||
|         scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError); | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == | ||||
|            SubGhzCustomEventManagerSet) { | ||||
|         if(scene_state == SubGhzCustomEventManagerSet) { | ||||
|             return false; | ||||
|         } else { | ||||
|             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; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubGhzCustomEventSceneShowErrorOk) { | ||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == | ||||
|                SubGhzCustomEventManagerSet) { | ||||
|             if(scene_state == SubGhzCustomEventManagerSet) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); | ||||
|             } | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventSceneShowErrorBack) { | ||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == | ||||
|                SubGhzCustomEventManagerSet) { | ||||
|             if(scene_state == SubGhzCustomEventManagerSet) { | ||||
|                 //exit app
 | ||||
|                 if(!scene_manager_previous_scene(subghz->scene_manager)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|  | ||||
| @ -70,7 +70,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|         if(event.event == SubmenuIndexReadRAW) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 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); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexRead) { | ||||
|  | ||||
| @ -11,32 +11,24 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { | ||||
| bool subghz_scene_transmitter_update_data_show(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     bool ret = false; | ||||
|     if(subghz->txrx->decoder_result) { | ||||
|         FuriString* key_str; | ||||
|         FuriString* frequency_str; | ||||
|         FuriString* modulation_str; | ||||
|     SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx); | ||||
| 
 | ||||
|         key_str = furi_string_alloc(); | ||||
|         frequency_str = furi_string_alloc(); | ||||
|         modulation_str = furi_string_alloc(); | ||||
|         uint8_t show_button = 0; | ||||
|     if(decoder) { | ||||
|         FuriString* key_str = furi_string_alloc(); | ||||
|         FuriString* frequency_str = furi_string_alloc(); | ||||
|         FuriString* modulation_str = furi_string_alloc(); | ||||
| 
 | ||||
|         if(subghz_protocol_decoder_base_deserialize( | ||||
|                subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) { | ||||
|             subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); | ||||
|                decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) { | ||||
|             subghz_protocol_decoder_base_get_string(decoder, key_str); | ||||
| 
 | ||||
|             if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == | ||||
|                SubGhzProtocolFlag_Send) { | ||||
|                 show_button = 1; | ||||
|             } | ||||
| 
 | ||||
|             subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); | ||||
|             subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||
|             subghz_view_transmitter_add_data_to_show( | ||||
|                 subghz->subghz_transmitter, | ||||
|                 furi_string_get_cstr(key_str), | ||||
|                 furi_string_get_cstr(frequency_str), | ||||
|                 furi_string_get_cstr(modulation_str), | ||||
|                 show_button); | ||||
|                 subghz_txrx_protocol_is_transmittable(subghz->txrx, false)); | ||||
|             ret = true; | ||||
|         } | ||||
|         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.event == SubGhzCustomEventViewTransmitterSendStart) { | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|             } | ||||
|             if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || | ||||
|                (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { | ||||
|                 if(subghz_tx_start(subghz, subghz->txrx->fff_data)) { | ||||
|                     subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|                     subghz_scene_transmitter_update_data_show(subghz); | ||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|                 } | ||||
| 
 | ||||
|             if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { | ||||
|                 subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|                 subghz_scene_transmitter_update_data_show(subghz); | ||||
|                 DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|             } | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             } | ||||
|             subghz_txrx_stop(subghz->txrx); | ||||
|             return true; | ||||
|         } else if(event.event == SubGhzCustomEventViewTransmitterBack) { | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|  | ||||
| @ -1,9 +1,6 @@ | ||||
| /* Abandon hope, all ye who enter here. */ | ||||
| 
 | ||||
| #include "subghz/types.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) { | ||||
|     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 = malloc(sizeof(SubGhz)); | ||||
| 
 | ||||
| @ -163,45 +150,18 @@ SubGhz* subghz_alloc() { | ||||
|         SubGhzViewIdStatic, | ||||
|         subghz_test_static_get_view(subghz->subghz_test_static)); | ||||
| 
 | ||||
|     //init setting
 | ||||
|     subghz->setting = subghz_setting_alloc(); | ||||
|     subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); | ||||
|     //init threshold rssi
 | ||||
|     subghz->threshold_rssi = subghz_threshold_rssi_alloc(); | ||||
| 
 | ||||
|     //init Worker & Protocol & History & KeyBoard
 | ||||
|     subghz->lock = SubGhzLockOff; | ||||
|     subghz->txrx = malloc(sizeof(SubGhzTxRx)); | ||||
|     subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); | ||||
|     subghz->txrx->preset->name = furi_string_alloc(); | ||||
|     subghz_preset_init( | ||||
|         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); | ||||
|     subghz_unlock(subghz); | ||||
|     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||
|     subghz->history = subghz_history_alloc(); | ||||
|     subghz->filter = SubGhzProtocolFlag_Decodable; | ||||
| 
 | ||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateSleep; | ||||
|     subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||
|     subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; | ||||
|     subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|     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 TxRx & History & KeyBoard
 | ||||
|     subghz->txrx = subghz_txrx_alloc(); | ||||
|     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||
|     subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); | ||||
| 
 | ||||
|     //Init Error_str
 | ||||
|     subghz->error_str = furi_string_alloc(); | ||||
| @ -219,7 +179,9 @@ void subghz_free(SubGhz* subghz) { | ||||
|         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
 | ||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); | ||||
| @ -282,18 +244,14 @@ void subghz_free(SubGhz* subghz) { | ||||
|     furi_record_close(RECORD_GUI); | ||||
|     subghz->gui = NULL; | ||||
| 
 | ||||
|     //setting
 | ||||
|     subghz_setting_free(subghz->setting); | ||||
|     // threshold rssi
 | ||||
|     subghz_threshold_rssi_free(subghz->threshold_rssi); | ||||
| 
 | ||||
|     //Worker & Protocol & History
 | ||||
|     subghz_receiver_free(subghz->txrx->receiver); | ||||
|     subghz_environment_free(subghz->txrx->environment); | ||||
|     subghz_worker_free(subghz->txrx->worker); | ||||
|     flipper_format_free(subghz->txrx->fff_data); | ||||
|     subghz_history_free(subghz->txrx->history); | ||||
|     furi_string_free(subghz->txrx->preset->name); | ||||
|     free(subghz->txrx->preset); | ||||
|     free(subghz->txrx); | ||||
|     subghz_history_free(subghz->history); | ||||
| 
 | ||||
|     //TxRx
 | ||||
|     subghz_txrx_free(subghz->txrx); | ||||
| 
 | ||||
|     //Error string
 | ||||
|     furi_string_free(subghz->error_str); | ||||
| @ -319,11 +277,6 @@ int32_t subghz_app(void* p) { | ||||
|         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
 | ||||
|     if(p && strlen(p)) { | ||||
|         uint32_t rpc_ctx = 0; | ||||
| @ -340,9 +293,9 @@ int32_t subghz_app(void* p) { | ||||
|             if(subghz_key_load(subghz, p, true)) { | ||||
|                 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
 | ||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; | ||||
|                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); | ||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||
|                 } else { | ||||
|                     //Load transmitter TX
 | ||||
| @ -358,7 +311,7 @@ int32_t subghz_app(void* p) { | ||||
|         view_dispatcher_attach_to_gui( | ||||
|             subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); | ||||
|         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); | ||||
|         } else { | ||||
|             scene_manager_set_scene_state( | ||||
|  | ||||
| @ -18,214 +18,42 @@ | ||||
| 
 | ||||
| #define TAG "SubGhz" | ||||
| 
 | ||||
| void subghz_preset_init( | ||||
|     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) { | ||||
| void subghz_set_default_preset(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     if(frequency != NULL) { | ||||
|         furi_string_printf( | ||||
|             frequency, | ||||
|             "%03ld.%02ld", | ||||
|             subghz->txrx->preset->frequency / 1000000 % 1000, | ||||
|             subghz->txrx->preset->frequency / 10000 % 100); | ||||
|     } | ||||
|     if(modulation != NULL) { | ||||
|         furi_string_printf(modulation, "%.2s", furi_string_get_cstr(subghz->txrx->preset->name)); | ||||
|     } | ||||
|     subghz_txrx_set_preset( | ||||
|         subghz->txrx, | ||||
|         "AM650", | ||||
|         subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)), | ||||
|         NULL, | ||||
|         0); | ||||
| } | ||||
| 
 | ||||
| void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { | ||||
| void subghz_blink_start(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     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); | ||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; | ||||
|     notification_message(subghz->notifications, &sequence_blink_stop); | ||||
|     notification_message(subghz->notifications, &sequence_blink_start_magenta); | ||||
| } | ||||
| 
 | ||||
| uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { | ||||
| void subghz_blink_stop(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||
|         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; | ||||
|     notification_message(subghz->notifications, &sequence_blink_stop); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     FuriString* temp_str; | ||||
|     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; | ||||
|         } | ||||
| 
 | ||||
|         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)); | ||||
|     default: | ||||
|         return true; | ||||
|         break; | ||||
|     } | ||||
|     subghz_idle(subghz); | ||||
|     subghz_speaker_off(subghz); | ||||
|     notification_message(subghz->notifications, &sequence_reset_red); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     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; | ||||
|     FuriString* temp_str; | ||||
|     temp_str = furi_string_alloc(); | ||||
|     FuriString* temp_str = furi_string_alloc(); | ||||
|     uint32_t temp_data32; | ||||
| 
 | ||||
|     do { | ||||
| @ -281,6 +109,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         //Load frequency
 | ||||
|         if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { | ||||
|             FURI_LOG_E(TAG, "Missing Frequency"); | ||||
|             break; | ||||
| @ -291,58 +120,61 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         subghz->txrx->preset->frequency = temp_data32; | ||||
| 
 | ||||
|         //Load preset
 | ||||
|         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { | ||||
|             FURI_LOG_E(TAG, "Missing Preset"); | ||||
|             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; | ||||
|         } | ||||
|         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
 | ||||
|             //delete preset if it already exists
 | ||||
|             subghz_setting_delete_custom_preset( | ||||
|                 subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); | ||||
|             subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); | ||||
|             //load custom preset from file
 | ||||
|             if(!subghz_setting_load_custom_preset( | ||||
|                    subghz->setting, | ||||
|                    furi_string_get_cstr(subghz->txrx->preset->name), | ||||
|                    fff_data_file)) { | ||||
|                    setting, furi_string_get_cstr(temp_str), fff_data_file)) { | ||||
|                 FURI_LOG_E(TAG, "Missing Custom preset"); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         size_t preset_index = subghz_setting_get_inx_preset_by_name( | ||||
|             subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); | ||||
|         subghz_preset_init( | ||||
|             subghz, | ||||
|             furi_string_get_cstr(subghz->txrx->preset->name), | ||||
|             subghz->txrx->preset->frequency, | ||||
|             subghz_setting_get_preset_data(subghz->setting, preset_index), | ||||
|             subghz_setting_get_preset_data_size(subghz->setting, preset_index)); | ||||
|         size_t preset_index = | ||||
|             subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); | ||||
|         subghz_txrx_set_preset( | ||||
|             subghz->txrx, | ||||
|             furi_string_get_cstr(temp_str), | ||||
|             temp_data32, | ||||
|             subghz_setting_get_preset_data(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)) { | ||||
|             FURI_LOG_E(TAG, "Missing Protocol"); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); | ||||
|         if(!strcmp(furi_string_get_cstr(temp_str), "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 { | ||||
|             subghz->load_type_file = SubGhzLoadTypeFileKey; | ||||
|             stream_copy_full( | ||||
|                 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( | ||||
|             subghz->txrx->receiver, furi_string_get_cstr(temp_str)); | ||||
|         if(subghz->txrx->decoder_result) { | ||||
|         if(subghz_txrx_load_decoder_by_name_protocol( | ||||
|                subghz->txrx, furi_string_get_cstr(temp_str))) { | ||||
|             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) { | ||||
|                 load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; | ||||
|                 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) { | ||||
|     furi_assert(subghz); | ||||
| 
 | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
|     FuriString* temp_str; | ||||
|     FuriString* file_name; | ||||
|     FuriString* file_path; | ||||
| 
 | ||||
|     temp_str = furi_string_alloc(); | ||||
|     file_name = furi_string_alloc(); | ||||
|     file_path = furi_string_alloc(); | ||||
|     FuriString* temp_str = furi_string_alloc(); | ||||
|     FuriString* file_name = furi_string_alloc(); | ||||
|     FuriString* file_path = furi_string_alloc(); | ||||
| 
 | ||||
|     bool res = false; | ||||
| 
 | ||||
| @ -438,8 +271,7 @@ bool subghz_save_protocol_to_file( | ||||
|     Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); | ||||
| 
 | ||||
|     bool saved = false; | ||||
|     FuriString* file_dir; | ||||
|     file_dir = furi_string_alloc(); | ||||
|     FuriString* file_dir = furi_string_alloc(); | ||||
| 
 | ||||
|     path_extract_dirname(dev_file_name, file_dir); | ||||
|     do { | ||||
| @ -467,11 +299,21 @@ bool subghz_save_protocol_to_file( | ||||
|     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) { | ||||
|     furi_assert(subghz); | ||||
| 
 | ||||
|     FuriString* file_path; | ||||
|     file_path = furi_string_alloc(); | ||||
|     FuriString* file_path = furi_string_alloc(); | ||||
| 
 | ||||
|     DialogsFileBrowserOptions browser_options; | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
| uint32_t subghz_random_serial(void) { | ||||
|     return (uint32_t)rand(); | ||||
| } | ||||
| 
 | ||||
| void subghz_hopper_update(SubGhz* subghz) { | ||||
| void subghz_lock(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
| 
 | ||||
|     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); | ||||
|     } | ||||
|     subghz->lock = SubGhzLockOn; | ||||
| } | ||||
| 
 | ||||
| void subghz_speaker_on(SubGhz* subghz) { | ||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { | ||||
|         if(furi_hal_speaker_acquire(30)) { | ||||
|             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); | ||||
|         } else { | ||||
|             subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; | ||||
|         } | ||||
|     } | ||||
| void subghz_unlock(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     subghz->lock = SubGhzLockOff; | ||||
| } | ||||
| 
 | ||||
| void subghz_speaker_off(SubGhz* subghz) { | ||||
|     if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { | ||||
|         if(furi_hal_speaker_is_mine()) { | ||||
|             furi_hal_subghz_set_async_mirror_pin(NULL); | ||||
|             furi_hal_speaker_release(); | ||||
|             if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) | ||||
|                 subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; | ||||
|         } | ||||
|     } | ||||
| bool subghz_is_locked(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     return (subghz->lock == SubGhzLockOn); | ||||
| } | ||||
| 
 | ||||
| void subghz_speaker_mute(SubGhz* subghz) { | ||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { | ||||
|         if(furi_hal_speaker_is_mine()) { | ||||
|             furi_hal_subghz_set_async_mirror_pin(NULL); | ||||
|         } | ||||
|     } | ||||
| void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) { | ||||
|     furi_assert(subghz); | ||||
|     subghz->rx_key_state = state; | ||||
| } | ||||
| 
 | ||||
| void subghz_speaker_unmute(SubGhz* subghz) { | ||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { | ||||
|         if(furi_hal_speaker_is_mine()) { | ||||
|             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); | ||||
|         } | ||||
|     } | ||||
| SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     return subghz->rx_key_state; | ||||
| } | ||||
|  | ||||
| @ -25,10 +25,6 @@ | ||||
| #include <gui/modules/widget.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" | ||||
| 
 | ||||
| @ -37,34 +33,12 @@ | ||||
| 
 | ||||
| #include "rpc/rpc_app.h" | ||||
| 
 | ||||
| #include "helpers/subghz_threshold_rssi.h" | ||||
| 
 | ||||
| #include "helpers/subghz_txrx.h" | ||||
| 
 | ||||
| #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 { | ||||
|     Gui* gui; | ||||
|     NotificationApp* notifications; | ||||
| @ -93,47 +67,43 @@ struct SubGhz { | ||||
|     SubGhzTestStatic* subghz_test_static; | ||||
|     SubGhzTestCarrier* subghz_test_carrier; | ||||
|     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 subghz_preset_init( | ||||
|     void* context, | ||||
|     const char* preset_name, | ||||
|     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); | ||||
| void subghz_set_default_preset(SubGhz* subghz); | ||||
| void subghz_blink_start(SubGhz* subghz); | ||||
| void subghz_blink_stop(SubGhz* subghz); | ||||
| 
 | ||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||
| void subghz_tx_stop(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_get_next_name_file(SubGhz* subghz, uint8_t max_len); | ||||
| bool subghz_save_protocol_to_file( | ||||
|     SubGhz* subghz, | ||||
|     FlipperFormat* flipper_format, | ||||
|     const char* dev_file_name); | ||||
| void subghz_save_to_file(void* context); | ||||
| bool subghz_load_protocol_from_file(SubGhz* subghz); | ||||
| bool subghz_rename_file(SubGhz* subghz); | ||||
| bool subghz_file_available(SubGhz* subghz); | ||||
| bool subghz_delete_file(SubGhz* subghz); | ||||
| void subghz_file_name_clear(SubGhz* subghz); | ||||
| bool subghz_path_is_file(FuriString* path); | ||||
| uint32_t subghz_random_serial(void); | ||||
| void subghz_hopper_update(SubGhz* subghz); | ||||
| void subghz_speaker_on(SubGhz* subghz); | ||||
| void subghz_speaker_off(SubGhz* subghz); | ||||
| void subghz_speaker_mute(SubGhz* subghz); | ||||
| void subghz_speaker_unmute(SubGhz* subghz); | ||||
| SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz); | ||||
| 
 | ||||
| void subghz_lock(SubGhz* subghz); | ||||
| void subghz_unlock(SubGhz* subghz); | ||||
| bool subghz_is_locked(SubGhz* subghz); | ||||
| 
 | ||||
| void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state); | ||||
| SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz); | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| #define MENU_ITEMS 4u | ||||
| #define UNLOCK_CNT 3 | ||||
| 
 | ||||
| #define SUBGHZ_RAW_TRESHOLD_MIN -90.0f | ||||
| #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f | ||||
| 
 | ||||
| typedef struct { | ||||
|     FuriString* item_str; | ||||
| @ -44,7 +44,7 @@ typedef enum { | ||||
| } SubGhzViewReceiverBarShow; | ||||
| 
 | ||||
| struct SubGhzViewReceiver { | ||||
|     SubGhzLock lock; | ||||
|     bool lock; | ||||
|     uint8_t lock_count; | ||||
|     FuriTimer* timer; | ||||
|     View* view; | ||||
| @ -70,20 +70,21 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { | ||||
|         instance->view, | ||||
|         SubGhzViewReceiverModel * model, | ||||
|         { | ||||
|             if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { | ||||
|             if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||
|                 model->u_rssi = 0; | ||||
|             } else { | ||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); | ||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); | ||||
|             } | ||||
|         }, | ||||
|         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); | ||||
|     subghz_receiver->lock_count = 0; | ||||
|     if(lock == SubGhzLockOn) { | ||||
|         subghz_receiver->lock = lock; | ||||
| 
 | ||||
|     if(lock == true) { | ||||
|         subghz_receiver->lock = true; | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
| @ -280,7 +281,7 @@ static void subghz_view_receiver_timer_callback(void* context) { | ||||
|         subghz_receiver->callback( | ||||
|             SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); | ||||
|     } else { | ||||
|         subghz_receiver->lock = SubGhzLockOff; | ||||
|         subghz_receiver->lock = false; | ||||
|         subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); | ||||
|     } | ||||
|     subghz_receiver->lock_count = 0; | ||||
| @ -290,7 +291,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzViewReceiver* subghz_receiver = context; | ||||
| 
 | ||||
|     if(subghz_receiver->lock == SubGhzLockOn) { | ||||
|     if(subghz_receiver->lock == true) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, | ||||
|             SubGhzViewReceiverModel * model, | ||||
| @ -310,7 +311,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | ||||
|                 SubGhzViewReceiverModel * model, | ||||
|                 { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, | ||||
|                 true); | ||||
|             //subghz_receiver->lock = SubGhzLockOff;
 | ||||
|             //subghz_receiver->lock = false;
 | ||||
|             furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); | ||||
|         } | ||||
| 
 | ||||
| @ -394,7 +395,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { | ||||
|     // View allocation and configuration
 | ||||
|     subghz_receiver->view = view_alloc(); | ||||
| 
 | ||||
|     subghz_receiver->lock = SubGhzLockOff; | ||||
|     subghz_receiver->lock = false; | ||||
|     subghz_receiver->lock_count = 0; | ||||
|     view_allocate_model( | ||||
|         subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); | ||||
|  | ||||
| @ -10,7 +10,7 @@ typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* contex | ||||
| 
 | ||||
| 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( | ||||
|     SubGhzViewReceiver* subghz_receiver, | ||||
|  | ||||
| @ -60,10 +60,10 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra | ||||
|     furi_assert(instance); | ||||
|     uint8_t u_rssi = 0; | ||||
| 
 | ||||
|     if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { | ||||
|     if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||
|         u_rssi = 0; | ||||
|     } 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( | ||||
| @ -261,9 +261,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod | ||||
|     uint8_t x = 118; | ||||
|     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; | ||||
|         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; | ||||
|         for(uint8_t i = 0; i < x; i += width * 2) { | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| #include <gui/view.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; | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,7 @@ typedef struct { | ||||
|     FuriString* frequency_str; | ||||
|     FuriString* preset_str; | ||||
|     FuriString* key_str; | ||||
|     uint8_t show_button; | ||||
|     bool show_button; | ||||
| } SubGhzViewTransmitterModel; | ||||
| 
 | ||||
| void subghz_view_transmitter_set_callback( | ||||
| @ -32,7 +32,7 @@ void subghz_view_transmitter_add_data_to_show( | ||||
|     const char* key_str, | ||||
|     const char* frequency_str, | ||||
|     const char* preset_str, | ||||
|     uint8_t show_button) { | ||||
|     bool show_button) { | ||||
|     furi_assert(subghz_transmitter); | ||||
|     with_view_model( | ||||
|         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->preset_str); | ||||
|                 furi_string_reset(model->key_str); | ||||
|                 model->show_button = 0; | ||||
|                 model->show_button = false; | ||||
|             }, | ||||
|             false); | ||||
|         return false; | ||||
|  | ||||
| @ -23,4 +23,4 @@ void subghz_view_transmitter_add_data_to_show( | ||||
|     const char* key_str, | ||||
|     const char* frequency_str, | ||||
|     const char* preset_str, | ||||
|     uint8_t show_button); | ||||
|     bool show_button); | ||||
|  | ||||
| @ -220,11 +220,9 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { | ||||
|     UNUSED(context); | ||||
|     if(!furi_string_cmp(args, "0")) { | ||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); | ||||
|         loader_update_menu(); | ||||
|         printf("Debug disabled."); | ||||
|     } else if(!furi_string_cmp(args, "1")) { | ||||
|         furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); | ||||
|         loader_update_menu(); | ||||
|         printf("Debug enabled."); | ||||
|     } else { | ||||
|         cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); | ||||
|  | ||||
| @ -6,6 +6,8 @@ | ||||
| #include <notification/notification_messages.h> | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <cli/cli.h> | ||||
| #include <cli/cli_vcp.h> | ||||
| 
 | ||||
| #include "animations/animation_manager.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_timeout.h" | ||||
| #include "desktop_i.h" | ||||
| #include "helpers/pin_lock.h" | ||||
| #include "helpers/pin.h" | ||||
| #include "helpers/slideshow_filename.h" | ||||
| 
 | ||||
| #define TAG "Desktop" | ||||
| @ -132,6 +134,15 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { | ||||
| } | ||||
| 
 | ||||
| void desktop_lock(Desktop* desktop) { | ||||
|     furi_hal_rtc_set_flag(FuriHalRtcFlagLock); | ||||
|     furi_hal_rtc_set_pin_fails(0); | ||||
| 
 | ||||
|     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); | ||||
|     scene_manager_set_scene_state( | ||||
|         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||
| @ -147,6 +158,13 @@ void desktop_unlock(Desktop* desktop) { | ||||
|     desktop_view_locked_unlock(desktop->locked_view); | ||||
|     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); | ||||
|     desktop_auto_lock_arm(desktop); | ||||
|     furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); | ||||
| 
 | ||||
|     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) { | ||||
| @ -290,11 +308,14 @@ Desktop* desktop_alloc() { | ||||
|     desktop->auto_lock_timer = | ||||
|         furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); | ||||
| 
 | ||||
|     furi_record_create(RECORD_DESKTOP, desktop); | ||||
| 
 | ||||
|     return desktop; | ||||
| } | ||||
| 
 | ||||
| void desktop_free(Desktop* desktop) { | ||||
|     furi_assert(desktop); | ||||
|     furi_check(furi_record_destroy(RECORD_DESKTOP)); | ||||
| 
 | ||||
|     furi_pubsub_unsubscribe( | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     UNUSED(p); | ||||
| 
 | ||||
| @ -375,14 +406,12 @@ int32_t desktop_srv(void* p) { | ||||
| 
 | ||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||
| 
 | ||||
|     desktop_pin_lock_init(&desktop->settings); | ||||
| 
 | ||||
|     if(!desktop_pin_lock_is_locked()) { | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { | ||||
|         desktop_lock(desktop); | ||||
|     } else { | ||||
|         if(!loader_is_locked(desktop->loader)) { | ||||
|             desktop_auto_lock_arm(desktop); | ||||
|         } | ||||
|     } else { | ||||
|         desktop_lock(desktop); | ||||
|     } | ||||
| 
 | ||||
|     if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { | ||||
|  | ||||
| @ -1,3 +1,9 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef struct Desktop Desktop; | ||||
| 
 | ||||
| #define RECORD_DESKTOP "desktop" | ||||
| 
 | ||||
| bool desktop_api_is_locked(Desktop* instance); | ||||
| 
 | ||||
| void desktop_api_unlock(Desktop* instance); | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
| #include <toolbox/saved_struct.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_MAGIC (0x17) | ||||
| @ -52,7 +52,6 @@ typedef struct { | ||||
|     FavoriteApp favorite_primary; | ||||
|     FavoriteApp favorite_secondary; | ||||
|     PinCode pin_code; | ||||
|     uint8_t is_locked; | ||||
|     uint32_t auto_lock_delay_ms; | ||||
|     uint8_t dummy_mode; | ||||
| } DesktopSettings; | ||||
|  | ||||
							
								
								
									
										74
									
								
								applications/services/desktop/helpers/pin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								applications/services/desktop/helpers/pin.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										11
									
								
								applications/services/desktop/helpers/pin.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/services/desktop/helpers/pin.h
									
									
									
									
									
										Normal 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); | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
| @ -10,7 +10,7 @@ | ||||
| #include "../views/desktop_view_lock_menu.h" | ||||
| #include "desktop_scene_i.h" | ||||
| #include "desktop_scene.h" | ||||
| #include "../helpers/pin_lock.h" | ||||
| #include "../helpers/pin.h" | ||||
| 
 | ||||
| #define TAG "DesktopSceneLock" | ||||
| 
 | ||||
| @ -25,7 +25,6 @@ void desktop_scene_lock_menu_on_enter(void* context) { | ||||
|     DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||
|     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_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_stealth_mode_state( | ||||
|         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) { | ||||
|             DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||
|             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); | ||||
|             } | ||||
|         } | ||||
| @ -55,21 +53,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|             desktop_lock(desktop); | ||||
|             consumed = true; | ||||
|             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: | ||||
|             desktop_set_dummy_mode_state(desktop, true); | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| 
 | ||||
| #include "../desktop.h" | ||||
| #include "../desktop_i.h" | ||||
| #include "../helpers/pin_lock.h" | ||||
| #include "../helpers/pin.h" | ||||
| #include "../animations/animation_manager.h" | ||||
| #include "../views/desktop_events.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; | ||||
|     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); | ||||
|     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); | ||||
|         Gui* gui = furi_record_open(RECORD_GUI); | ||||
|         gui_set_lockdown(gui, true); | ||||
|  | ||||
| @ -106,10 +106,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case DesktopMainEventOpenMenu: | ||||
|             loader_show_menu(); | ||||
|         case DesktopMainEventOpenMenu: { | ||||
|             Loader* loader = furi_record_open(RECORD_LOADER); | ||||
|             loader_show_menu(loader); | ||||
|             furi_record_close(RECORD_LOADER); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         } break; | ||||
| 
 | ||||
|         case DesktopMainEventOpenLockMenu: | ||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| #include "../animations/animation_manager.h" | ||||
| #include "../views/desktop_events.h" | ||||
| #include "../views/desktop_view_pin_input.h" | ||||
| #include "../helpers/pin_lock.h" | ||||
| #include "../helpers/pin.h" | ||||
| #include "desktop_scene.h" | ||||
| #include "desktop_scene_i.h" | ||||
| 
 | ||||
| @ -54,9 +54,12 @@ static void desktop_scene_pin_input_back_callback(void* context) { | ||||
| 
 | ||||
| static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* 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)) { | ||||
|         furi_hal_rtc_set_pin_fails(0); | ||||
|         view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); | ||||
|     } 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( | ||||
|             desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); | ||||
|     } | ||||
| @ -126,7 +129,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case DesktopPinInputEventUnlocked: | ||||
|             desktop_pin_unlock(&desktop->settings); | ||||
|             desktop_unlock(desktop); | ||||
|             consumed = true; | ||||
|             break; | ||||
|  | ||||
| @ -31,7 +31,6 @@ typedef enum { | ||||
|     DesktopDebugEventExit, | ||||
| 
 | ||||
|     DesktopLockMenuEventLock, | ||||
|     DesktopLockMenuEventPinLock, | ||||
|     DesktopLockMenuEventDummyModeOn, | ||||
|     DesktopLockMenuEventDummyModeOff, | ||||
|     DesktopLockMenuEventStealthModeOn, | ||||
|  | ||||
| @ -65,13 +65,16 @@ void desktop_debug_render(Canvas* canvas, void* model) { | ||||
|             version_get_builddate(ver)); | ||||
|         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( | ||||
|             buffer, | ||||
|             sizeof(buffer), | ||||
|             "%s%s [%s] %s", | ||||
|             "%s%s [%d.%d] %s", | ||||
|             version_get_dirty_flag(ver) ? "[!] " : "", | ||||
|             version_get_githash(ver), | ||||
|             version_get_gitbranchnum(ver), | ||||
|             api_major, | ||||
|             api_minor, | ||||
|             c2_ver ? c2_ver->StackTypeString : "<none>"); | ||||
|         canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); | ||||
| 
 | ||||
|  | ||||
| @ -23,14 +23,6 @@ void desktop_lock_menu_set_callback( | ||||
|     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) { | ||||
|     with_view_model( | ||||
|         lock_menu->view, | ||||
| @ -102,7 +94,6 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | ||||
|     bool consumed = false; | ||||
|     bool dummy_mode = false; | ||||
|     bool stealth_mode = false; | ||||
|     bool pin_is_set = false; | ||||
|     bool update = false; | ||||
| 
 | ||||
|     with_view_model( | ||||
| @ -131,15 +122,12 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | ||||
|             idx = model->idx; | ||||
|             dummy_mode = model->dummy_mode; | ||||
|             stealth_mode = model->stealth_mode; | ||||
|             pin_is_set = model->pin_is_set; | ||||
|         }, | ||||
|         update); | ||||
| 
 | ||||
|     if(event->key == InputKeyOk) { | ||||
|         if((idx == DesktopLockMenuIndexLock)) { | ||||
|             if((pin_is_set) && (event->type == InputTypeShort)) { | ||||
|                 lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); | ||||
|             } else if((pin_is_set == false) && (event->type == InputTypeShort)) { | ||||
|             if((event->type == InputTypeShort)) { | ||||
|                 lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); | ||||
|             } | ||||
|         } else if(idx == DesktopLockMenuIndexStealth) { | ||||
|  | ||||
| @ -17,7 +17,6 @@ struct DesktopLockMenuView { | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t idx; | ||||
|     bool pin_is_set; | ||||
|     bool dummy_mode; | ||||
|     bool stealth_mode; | ||||
| } DesktopLockMenuViewModel; | ||||
| @ -28,7 +27,6 @@ void desktop_lock_menu_set_callback( | ||||
|     void* context); | ||||
| 
 | ||||
| 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_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); | ||||
| void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
| @ -154,6 +154,8 @@ Menu* menu_alloc() { | ||||
| void menu_free(Menu* menu) { | ||||
|     furi_assert(menu); | ||||
|     menu_reset(menu); | ||||
|     with_view_model( | ||||
|         menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); | ||||
|     view_free(menu->view); | ||||
|     free(menu); | ||||
| } | ||||
|  | ||||
| @ -19,19 +19,16 @@ void view_tie_icon_animation(View* view, IconAnimation* icon_animation) { | ||||
| 
 | ||||
| void view_set_draw_callback(View* view, ViewDrawCallback callback) { | ||||
|     furi_assert(view); | ||||
|     furi_assert(view->draw_callback == NULL); | ||||
|     view->draw_callback = callback; | ||||
| } | ||||
| 
 | ||||
| void view_set_input_callback(View* view, ViewInputCallback callback) { | ||||
|     furi_assert(view); | ||||
|     furi_assert(view->input_callback == NULL); | ||||
|     view->input_callback = callback; | ||||
| } | ||||
| 
 | ||||
| void view_set_custom_callback(View* view, ViewCustomCallback callback) { | ||||
|     furi_assert(view); | ||||
|     furi_assert(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) { | ||||
|     furi_assert(view); | ||||
|     furi_assert(context); | ||||
|     view->context = context; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ App( | ||||
|     entry_point="loader_srv", | ||||
|     cdefines=["SRV_LOADER"], | ||||
|     requires=["gui"], | ||||
|     provides=["loader_start"], | ||||
|     stack_size=2 * 1024, | ||||
|     order=90, | ||||
|     sdk_headers=[ | ||||
| @ -12,3 +13,11 @@ App( | ||||
|         "firmware_api/firmware_api.h", | ||||
|     ], | ||||
| ) | ||||
| 
 | ||||
| App( | ||||
|     appid="loader_start", | ||||
|     apptype=FlipperAppType.STARTUP, | ||||
|     entry_point="loader_on_system_start", | ||||
|     requires=["loader"], | ||||
|     order=90, | ||||
| ) | ||||
|  | ||||
| @ -6,6 +6,8 @@ | ||||
| /* Generated table */ | ||||
| #include <firmware_api_table.h> | ||||
| 
 | ||||
| #include <furi_hal_info.h> | ||||
| 
 | ||||
| static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); | ||||
| 
 | ||||
| constexpr HashtableApiInterface elf_api_interface{ | ||||
| @ -19,3 +21,8 @@ constexpr HashtableApiInterface 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; | ||||
| } | ||||
| @ -1,76 +1,114 @@ | ||||
| #include "applications.h" | ||||
| #include <furi.h> | ||||
| #include "loader/loader.h" | ||||
| #include "loader.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) | ||||
| #define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) | ||||
| LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { | ||||
|     LoaderMessage message; | ||||
|     LoaderMessageLoaderStatusResult result; | ||||
| 
 | ||||
| static Loader* loader_instance = NULL; | ||||
| 
 | ||||
| static bool | ||||
|     loader_start_application(const FlipperApplication* application, const char* arguments) { | ||||
|     loader_instance->application = application; | ||||
| 
 | ||||
|     furi_assert(loader_instance->application_arguments == NULL); | ||||
|     if(arguments && strlen(arguments) > 0) { | ||||
|         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; | ||||
|     message.type = LoaderMessageTypeStartByName; | ||||
|     message.start.name = name; | ||||
|     message.start.args = args; | ||||
|     message.api_lock = api_lock_alloc_locked(); | ||||
|     message.status_value = &result; | ||||
|     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||
|     api_lock_wait_unlock_and_free(message.api_lock); | ||||
|     return result.value; | ||||
| } | ||||
| 
 | ||||
| static void loader_menu_callback(void* _ctx, uint32_t index) { | ||||
|     UNUSED(index); | ||||
|     const FlipperApplication* application = _ctx; | ||||
| bool loader_lock(Loader* loader) { | ||||
|     LoaderMessage message; | ||||
|     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); | ||||
|     furi_assert(application->name); | ||||
| void loader_unlock(Loader* loader) { | ||||
|     LoaderMessage message; | ||||
|     message.type = LoaderMessageTypeUnlock; | ||||
|     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||
| } | ||||
| 
 | ||||
|     if(!loader_lock(loader_instance)) { | ||||
|         FURI_LOG_E(TAG, "Loader is locked"); | ||||
|         return; | ||||
| bool loader_is_locked(Loader* loader) { | ||||
|     LoaderMessage message; | ||||
|     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) { | ||||
|     UNUSED(index); | ||||
|     uint32_t view_id = (uint32_t)context; | ||||
|     view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); | ||||
| } | ||||
| // implementation
 | ||||
| 
 | ||||
| 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 Loader* loader_alloc() { | ||||
|     Loader* loader = malloc(sizeof(Loader)); | ||||
|     loader->pubsub = furi_pubsub_alloc(); | ||||
|     loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); | ||||
|     loader->loader_menu = NULL; | ||||
|     loader->app.args = NULL; | ||||
|     loader->app.name = NULL; | ||||
|     loader->app.thread = NULL; | ||||
|     loader->app.insomniac = false; | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); | ||||
|     if(!application) { | ||||
| @ -100,346 +138,168 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { | ||||
|     return application; | ||||
| } | ||||
| 
 | ||||
| static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { | ||||
|     UNUSED(cli); | ||||
|     if(loader_is_locked(instance)) { | ||||
|         if(instance->application) { | ||||
|             furi_assert(instance->application->name); | ||||
|             printf("Can't start, %s application is running", instance->application->name); | ||||
|         } else { | ||||
|             printf("Can't start, furi application is running"); | ||||
|         } | ||||
|         return; | ||||
| static void | ||||
|     loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { | ||||
|     FURI_LOG_I(TAG, "Starting %s", app->name); | ||||
| 
 | ||||
|     // store args
 | ||||
|     furi_assert(loader->app.args == NULL); | ||||
|     if(args && strlen(args) > 0) { | ||||
|         loader->app.args = strdup(args); | ||||
|     } | ||||
| 
 | ||||
|     FuriString* application_name; | ||||
|     application_name = furi_string_alloc(); | ||||
|     // store name
 | ||||
|     furi_assert(loader->app.name == NULL); | ||||
|     loader->app.name = strdup(app->name); | ||||
| 
 | ||||
|     do { | ||||
|         if(!args_read_probably_quoted_string_and_trim(args, application_name)) { | ||||
|             printf("No application provided\r\n"); | ||||
|             break; | ||||
|         } | ||||
|     // setup app thread
 | ||||
|     loader->app.thread = | ||||
|         furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); | ||||
|     furi_thread_set_appid(loader->app.thread, app->appid); | ||||
| 
 | ||||
|         const FlipperApplication* application = | ||||
|             loader_find_application_by_name(furi_string_get_cstr(application_name)); | ||||
|         if(!application) { | ||||
|             printf("%s doesn't exists\r\n", furi_string_get_cstr(application_name)); | ||||
|             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"); | ||||
|     // setup heap trace
 | ||||
|     FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); | ||||
|     if(mode > FuriHalRtcHeapTrackModeNone) { | ||||
|         furi_thread_enable_heap_trace(loader->app.thread); | ||||
|     } else { | ||||
|         printf("Running application: "); | ||||
|         if(instance->application) { | ||||
|             furi_assert(instance->application->name); | ||||
|             printf("%s\r\n", instance->application->name); | ||||
|         } else { | ||||
|             printf("unknown\r\n"); | ||||
|         } | ||||
|         furi_thread_disable_heap_trace(loader->app.thread); | ||||
|     } | ||||
| 
 | ||||
|     // setup insomnia
 | ||||
|     if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { | ||||
|         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) { | ||||
|     furi_assert(_ctx); | ||||
|     Loader* instance = _ctx; | ||||
| 
 | ||||
|     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(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); | ||||
| static void loader_do_menu_closed(Loader* loader) { | ||||
|     if(loader->loader_menu) { | ||||
|         loader_menu_stop(loader->loader_menu); | ||||
|         loader_menu_free(loader->loader_menu); | ||||
|         loader->loader_menu = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | ||||
|     UNUSED(instance); | ||||
|     furi_assert(name); | ||||
| static bool loader_do_is_locked(Loader* loader) { | ||||
|     return loader->app.thread != NULL; | ||||
| } | ||||
| 
 | ||||
|     const FlipperApplication* application = loader_find_application_by_name(name); | ||||
| 
 | ||||
|     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"); | ||||
| static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { | ||||
|     if(loader_do_is_locked(loader)) { | ||||
|         return LoaderStatusErrorAppStarted; | ||||
|     } | ||||
| 
 | ||||
|     if(!loader_start_application(application, args)) { | ||||
|         return LoaderStatusErrorInternal; | ||||
|     const FlipperApplication* app = loader_find_application_by_name(name); | ||||
| 
 | ||||
|     if(!app) { | ||||
|         return LoaderStatusErrorUnknownApp; | ||||
|     } | ||||
| 
 | ||||
|     loader_start_internal_app(loader, app, args); | ||||
|     return LoaderStatusOk; | ||||
| } | ||||
| 
 | ||||
| bool loader_lock(Loader* instance) { | ||||
|     FURI_CRITICAL_ENTER(); | ||||
|     bool result = 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); | ||||
| static bool loader_do_lock(Loader* loader) { | ||||
|     if(loader->app.thread) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     furi_pubsub_free(instance->pubsub); | ||||
| 
 | ||||
|     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; | ||||
|     loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void loader_build_menu() { | ||||
|     FURI_LOG_I(TAG, "Building main menu"); | ||||
|     size_t i; | ||||
|     for(i = 0; i < FLIPPER_APPS_COUNT; i++) { | ||||
|         menu_add_item( | ||||
|             loader_instance->primary_menu, | ||||
|             FLIPPER_APPS[i].name, | ||||
|             FLIPPER_APPS[i].icon, | ||||
|             i, | ||||
|             loader_menu_callback, | ||||
|             (void*)&FLIPPER_APPS[i]); | ||||
| static void loader_do_unlock(Loader* loader) { | ||||
|     furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); | ||||
|     loader->app.thread = NULL; | ||||
| } | ||||
| 
 | ||||
| static void loader_do_app_closed(Loader* loader) { | ||||
|     furi_assert(loader->app.thread); | ||||
|     FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); | ||||
|     if(loader->app.args) { | ||||
|         free(loader->app.args); | ||||
|         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() { | ||||
|     FURI_LOG_I(TAG, "Building settings menu"); | ||||
|     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]); | ||||
|     if(loader->app.insomniac) { | ||||
|         furi_hal_power_insomnia_exit(); | ||||
|     } | ||||
| 
 | ||||
|     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() { | ||||
|     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(); | ||||
| } | ||||
| // app
 | ||||
| 
 | ||||
| int32_t loader_srv(void* p) { | ||||
|     UNUSED(p); | ||||
|     Loader* loader = loader_alloc(); | ||||
|     furi_record_create(RECORD_LOADER, loader); | ||||
| 
 | ||||
|     FURI_LOG_I(TAG, "Executing system start hooks"); | ||||
|     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; 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)) { | ||||
|         loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); | ||||
|         loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); | ||||
|     } | ||||
| 
 | ||||
|     while(1) { | ||||
|         uint32_t flags = | ||||
|             furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); | ||||
|         if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { | ||||
|             menu_set_selected_item(loader_instance->primary_menu, 0); | ||||
|             view_dispatcher_switch_to_view( | ||||
|                 loader_instance->view_dispatcher, LoaderMenuViewPrimary); | ||||
|             view_dispatcher_run(loader_instance->view_dispatcher); | ||||
|     LoaderMessage message; | ||||
|     while(true) { | ||||
|         if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { | ||||
|             switch(message.type) { | ||||
|             case LoaderMessageTypeStartByName: | ||||
|                 message.status_value->value = | ||||
|                     loader_do_start_by_name(loader, message.start.name, message.start.args); | ||||
|                 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; | ||||
| } | ||||
| 
 | ||||
| FuriPubSub* loader_get_pubsub(Loader* instance) { | ||||
|     return instance->pubsub; | ||||
| } | ||||
| } | ||||
| @ -1,7 +1,5 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <core/pubsub.h> | ||||
| #include <stdbool.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| @ -43,17 +41,14 @@ bool loader_lock(Loader* instance); | ||||
| void loader_unlock(Loader* instance); | ||||
| 
 | ||||
| /** Get loader lock status */ | ||||
| bool loader_is_locked(const Loader* instance); | ||||
| bool loader_is_locked(Loader* instance); | ||||
| 
 | ||||
| /** Show primary loader */ | ||||
| void loader_show_menu(); | ||||
| 
 | ||||
| /** Show primary loader */ | ||||
| void loader_update_menu(); | ||||
| void loader_show_menu(Loader* instance); | ||||
| 
 | ||||
| /** Show primary loader */ | ||||
| FuriPubSub* loader_get_pubsub(Loader* instance); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										117
									
								
								applications/services/loader/loader_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								applications/services/loader/loader_cli.c
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
| @ -1,39 +1,56 @@ | ||||
| #include "loader.h" | ||||
| 
 | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <core/pubsub.h> | ||||
| #include <cli/cli.h> | ||||
| #include <lib/toolbox/args.h> | ||||
| #include <toolbox/api_lock.h> | ||||
| #include "loader.h" | ||||
| #include "loader_menu.h" | ||||
| 
 | ||||
| #include <gui/view_dispatcher.h> | ||||
| 
 | ||||
| #include <gui/modules/menu.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| 
 | ||||
| #include <applications.h> | ||||
| #include <assets_icons.h> | ||||
| typedef struct { | ||||
|     char* args; | ||||
|     char* name; | ||||
|     FuriThread* thread; | ||||
|     bool insomniac; | ||||
| } LoaderAppData; | ||||
| 
 | ||||
| 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; | ||||
|     FuriMessageQueue* queue; | ||||
|     LoaderMenu* loader_menu; | ||||
|     LoaderAppData app; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     LoaderMenuViewPrimary, | ||||
|     LoaderMenuViewSettings, | ||||
| } LoaderMenuView; | ||||
|     LoaderMessageTypeStartByName, | ||||
|     LoaderMessageTypeAppClosed, | ||||
|     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; | ||||
|  | ||||
							
								
								
									
										187
									
								
								applications/services/loader/loader_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								applications/services/loader/loader_menu.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										30
									
								
								applications/services/loader/loader_menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/services/loader/loader_menu.h
									
									
									
									
									
										Normal 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 | ||||
| @ -57,6 +57,10 @@ static RpcSystemCallbacks rpc_systems[] = { | ||||
|         .alloc = rpc_system_property_alloc, | ||||
|         .free = NULL, | ||||
|     }, | ||||
|     { | ||||
|         .alloc = rpc_desktop_alloc, | ||||
|         .free = rpc_desktop_free, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| struct RpcSession { | ||||
| @ -326,31 +330,35 @@ static int32_t rpc_session_worker(void* context) { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
| static void rpc_session_thread_pending_callback(void* context, uint32_t arg) { | ||||
|     UNUSED(arg); | ||||
|     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) { | ||||
|         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_free(session->thread); | ||||
|         free(session); | ||||
|         furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -385,7 +393,7 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { | ||||
|     session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, 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); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										73
									
								
								applications/services/rpc/rpc_desktop.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								applications/services/rpc/rpc_desktop.c
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
| @ -36,6 +36,9 @@ void* rpc_system_gpio_alloc(RpcSession* session); | ||||
| void rpc_system_gpio_free(void* ctx); | ||||
| 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_data(const char* prefix, uint8_t* buffer, size_t size); | ||||
| 
 | ||||
|  | ||||
| @ -803,6 +803,7 @@ void storage_file_free(File* file) { | ||||
| } | ||||
| 
 | ||||
| FuriPubSub* storage_get_pubsub(Storage* storage) { | ||||
|     furi_assert(storage); | ||||
|     return storage->pubsub; | ||||
| } | ||||
| 
 | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov