diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt
new file mode 100644
index 000000000..a6c7ca694
--- /dev/null
+++ b/.ci_files/anims_ofw.txt
@@ -0,0 +1,128 @@
+Filetype: Flipper Animation Manifest
+Version: 1
+
+Name: L1_Waves_128x50
+Min butthurt: 0
+Max butthurt: 5
+Min level: 1
+Max level: 3
+Weight: 3
+
+Name: L1_Laptop_128x51
+Min butthurt: 0
+Max butthurt: 7
+Min level: 1
+Max level: 1
+Weight: 3
+
+Name: L1_Sleep_128x64
+Min butthurt: 0
+Max butthurt: 10
+Min level: 1
+Max level: 3
+Weight: 3
+
+Name: L1_Recording_128x51
+Min butthurt: 0
+Max butthurt: 8
+Min level: 1
+Max level: 1
+Weight: 3
+
+Name: L1_Furippa1_128x64
+Min butthurt: 0
+Max butthurt: 6
+Min level: 1
+Max level: 1
+Weight: 3
+
+Name: L2_Furippa2_128x64
+Min butthurt: 0
+Max butthurt: 6
+Min level: 2
+Max level: 2
+Weight: 3
+
+Name: L3_Furippa3_128x64
+Min butthurt: 0
+Max butthurt: 6
+Min level: 3
+Max level: 3
+Weight: 3
+
+Name: L1_Read_books_128x64
+Min butthurt: 0
+Max butthurt: 8
+Min level: 1
+Max level: 1
+Weight: 3
+
+Name: L2_Hacking_pc_128x64
+Min butthurt: 0
+Max butthurt: 8
+Min level: 2
+Max level: 2
+Weight: 3
+
+Name: L1_Cry_128x64
+Min butthurt: 8
+Max butthurt: 13
+Min level: 1
+Max level: 3
+Weight: 3
+
+Name: L1_Boxing_128x64
+Min butthurt: 10
+Max butthurt: 13
+Min level: 1
+Max level: 3
+Weight: 3
+
+Name: L1_Mad_fist_128x64
+Min butthurt: 9
+Max butthurt: 13
+Min level: 1
+Max level: 3
+Weight: 3
+
+Name: L1_Mods_128x64
+Min butthurt: 0
+Max butthurt: 9
+Min level: 1
+Max level: 3
+Weight: 5
+
+Name: L1_Painting_128x64
+Min butthurt: 0
+Max butthurt: 7
+Min level: 1
+Max level: 3
+Weight: 4
+
+Name: L3_Hijack_radio_128x64
+Min butthurt: 0
+Max butthurt: 8
+Min level: 3
+Max level: 3
+Weight: 3
+
+Name: L3_Lab_research_128x54
+Min butthurt: 0
+Max butthurt: 10
+Min level: 3
+Max level: 3
+Weight: 3
+
+Name: L2_Soldering_128x64
+Min butthurt: 0
+Max butthurt: 10
+Min level: 2
+Max level: 2
+Weight: 3
+
+Name: L1_Leaving_sad_128x64
+Min butthurt: 14
+Max butthurt: 14
+Min level: 1
+Max level: 3
+Weight: 3
diff --git a/.drone.yml b/.drone.yml
index 5b14639d4..d46354045 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1,6 +1,6 @@
kind: pipeline
type: docker
-name: "Build firmware"
+name: "Release firmware"
steps:
- name: "Update submodules"
@@ -11,19 +11,38 @@ steps:
- git submodule foreach git config --local gc.auto 0
- git log -1 --format='%H'
- - name: "Build default fw"
+ - name: "Build firmware"
image: hfdj/fztools
pull: never
commands:
- export DIST_SUFFIX=${DRONE_TAG}
- - export WORKFLOW_BRANCH_OR_TAG=dev-cfw
+ - export WORKFLOW_BRANCH_OR_TAG=release-cfw
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-default
- mv dist/f7-C/* artifacts-default/
- ls -laS artifacts-default
- ls -laS artifacts-default/f7-update-${DRONE_TAG}
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
- - echo '# [Install via Web Updater](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=dev-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md
+ - echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md
+ environment:
+ FBT_TOOLS_CUSTOM_LINK:
+ from_secret: fbt_link
+
+ - name: "Build no anims FW"
+ image: hfdj/fztools
+ pull: never
+ commands:
+ - rm -f assets/dolphin/external/manifest.txt
+ - cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt
+ - export DIST_SUFFIX=${DRONE_TAG}n
+ - export WORKFLOW_BRANCH_OR_TAG=no-custom-anims
+ - ./fbt COMPACT=1 DEBUG=0 updater_package
+ - mkdir artifacts-ofw-anims
+ - mv dist/f7-C/* artifacts-ofw-anims/
+ - ls -laS artifacts-ofw-anims
+ - ls -laS artifacts-ofw-anims/f7-update-${DRONE_TAG}n
+ - echo '' >> CHANGELOG.md
+ - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md
environment:
FBT_TOOLS_CUSTOM_LINK:
from_secret: fbt_link
@@ -31,12 +50,19 @@ steps:
- name: "Bundle self-update packages"
image: kramos/alpine-zip
commands:
+ - cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz .
- cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz .
+ - zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n
- zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG}
+ - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts debug
+ - rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG}
- rm -rf artifacts-default/f7-update-${DRONE_TAG}
+ - ls -laS artifacts-ofw-anims
- ls -laS artifacts-default
+ - mv artifacts-default/ ${DRONE_TAG}
+ - ls -laS ${DRONE_TAG}
- - name: "Upload to updates server"
+ - name: "Upload default to updates srv"
image: appleboy/drone-scp
settings:
host:
@@ -48,8 +74,29 @@ steps:
port:
from_secret: dep_port
target:
- from_secret: dep_target
- source: flipper-z-f7-update-${DRONE_TAG}.tgz
+ from_secret: dep_target_new
+ source:
+ - ${DRONE_TAG}/*.tgz
+ - ${DRONE_TAG}/*.zip
+ - ${DRONE_TAG}/*.json
+ - ${DRONE_TAG}/*.elf
+ - ${DRONE_TAG}/*.dfu
+ - ${DRONE_TAG}/*.bin
+
+ - name: "Upload no-anims to updates srv"
+ image: appleboy/drone-scp
+ settings:
+ host:
+ from_secret: dep_host
+ username:
+ from_secret: dep_user
+ password:
+ from_secret: dep_passwd
+ port:
+ from_secret: dep_port
+ target:
+ from_secret: dep_target_noanim
+ source: flipper-z-f7-update-${DRONE_TAG}n.tgz
- name: "Do Github release"
image: ddplugins/github-release
@@ -61,8 +108,9 @@ steps:
api_key:
from_secret: github_apikey
files:
- - artifacts-default/*.tgz
- - artifacts-default/*.zip
+ - ${DRONE_TAG}/*.tgz
+ - ${DRONE_TAG}/*.zip
+ - artifacts-ofw-anims/*.tgz
title: ${DRONE_TAG}
note: CHANGELOG.md
checksum:
@@ -70,6 +118,17 @@ steps:
- sha1
- crc32
+ - name: "Trigger update server reindex"
+ image: hfdj/fztools
+ pull: never
+ environment:
+ UPD_KEY:
+ from_secret: git_update_serv_token
+ UPD_URL:
+ from_secret: git_update_server_url
+ commands:
+ - curl -X POST -F 'key='$UPD_KEY'' $UPD_URL
+
- name: "Send files to telegram"
image: appleboy/drone-telegram
settings:
@@ -87,9 +146,18 @@ steps:
[-Github-](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG})
- [-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
+ [-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
+
+
+ [-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
+
+
+ [-Version without custom animations - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n)
+
+
+ [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})"
document:
- - artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz
+ - ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz
- name: "Send discord notification"
image: appleboy/drone-discord
@@ -107,7 +175,16 @@ steps:
[[Github]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG})
- [-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
+ [-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
+
+
+ [-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
+
+
+ [-Version without custom animations - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n)
+
+
+ [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})"
trigger:
event:
@@ -115,3 +192,121 @@ trigger:
node:
typ: haupt
+
+---
+kind: pipeline
+type: docker
+name: "Dev build"
+
+steps:
+ - name: "Update submodules"
+ image: alpine/git
+ commands:
+ - git submodule sync
+ - git -c protocol.version=2 submodule update --init --force --recursive
+ - git submodule foreach git config --local gc.auto 0
+ - git log -1 --format='%H'
+
+ - name: "Build dev FW"
+ image: hfdj/fztools
+ pull: never
+ commands:
+ - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}
+ - export WORKFLOW_BRANCH_OR_TAG=dev-cfw
+ - ./fbt COMPACT=1 DEBUG=0 updater_package
+ - mkdir artifacts-default
+ - mv dist/f7-C/* artifacts-default/
+ - ls -laS artifacts-default
+ - ls -laS artifacts-default/f7-update-${DRONE_BUILD_NUMBER}
+ environment:
+ FBT_TOOLS_CUSTOM_LINK:
+ from_secret: fbt_link
+
+ - name: "Bundle self-update packages"
+ image: kramos/alpine-zip
+ commands:
+ - cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz .
+ - rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER}
+ - ls -laS artifacts-default
+ - mv artifacts-default/ dev
+ - ls -laS dev
+
+ - name: "Clean dev folder"
+ image: appleboy/drone-ssh
+ settings:
+ host:
+ from_secret: dep_host
+ username:
+ from_secret: dep_user
+ password:
+ from_secret: dep_passwd
+ port:
+ from_secret: dep_port
+ command_timeout: 30s
+ script:
+ - cd web/unleashedflip.com/public_html/fw/dev
+ - rm -f ./*
+
+ - name: "Upload default to updates srv"
+ image: appleboy/drone-scp
+ settings:
+ host:
+ from_secret: dep_host
+ username:
+ from_secret: dep_user
+ password:
+ from_secret: dep_passwd
+ port:
+ from_secret: dep_port
+ target:
+ from_secret: dep_target_new
+ source:
+ - dev/*.tgz
+ - dev/*.zip
+ - dev/*.json
+ - dev/*.elf
+ - dev/*.dfu
+ - dev/*.bin
+
+ - name: "Trigger update server reindex"
+ image: hfdj/fztools
+ pull: never
+ environment:
+ UPD_KEY:
+ from_secret: git_update_serv_token
+ UPD_URL:
+ from_secret: git_update_server_url
+ commands:
+ - curl -X POST -F 'key='$UPD_KEY'' $UPD_URL
+
+ - name: "Send files to telegram"
+ image: appleboy/drone-telegram
+ settings:
+ token:
+ from_secret: tgtoken
+ to:
+ from_secret: tgid_dev
+ format: markdown
+ message: "Unleashed firmware dev build successful!
+
+
+ Build: {{build.number}}
+
+ SHA: {{commit.sha}}
+
+
+ Commit: {{commit.message}}
+
+
+ [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})"
+ document:
+ - dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz
+
+trigger:
+ branch:
+ - dev
+ event:
+ - push
+
+node:
+ typ: haupt
diff --git a/.gitignore b/.gitignore
index 8f4ab0e1f..813505b50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,5 @@ openocd.log
# PVS Studio temporary files
.PVS-Studio/
PVS-Studio.log
+
+.gdbinit
diff --git a/.gitmodules b/.gitmodules
index ba7644981..a97e0933a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,12 +22,12 @@
[submodule "lib/microtar"]
path = lib/microtar
url = https://github.com/amachronic/microtar.git
-[submodule "lib/scons"]
- path = lib/scons
- url = https://github.com/SCons/scons.git
[submodule "lib/mbedtls"]
path = lib/mbedtls
url = https://github.com/Mbed-TLS/mbedtls.git
[submodule "lib/cxxheaderparser"]
path = lib/cxxheaderparser
url = https://github.com/robotpy/cxxheaderparser.git
+[submodule "applications/plugins/dap_link/lib/free-dap"]
+ path = applications/plugins/dap_link/lib/free-dap
+ url = https://github.com/ataradov/free-dap.git
diff --git a/.pvsconfig b/.pvsconfig
index d17eaa5a0..5f1ffb7cb 100644
--- a/.pvsconfig
+++ b/.pvsconfig
@@ -5,6 +5,7 @@
//-V:BPTREE_DEF2:779,1086,557,773,512
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
//-V:ALGO_DEF:1048,747,1044
+//-V:TUPLE_DEF2:524,590,1001,760
# Non-severe malloc/null pointer deref warnings
//-V::522:2,3
diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json
index db08320c9..d1cac63e9 100644
--- a/.vscode/example/c_cpp_properties.json
+++ b/.vscode/example/c_cpp_properties.json
@@ -2,7 +2,7 @@
"configurations": [
{
"name": "Win32",
- "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe",
+ "compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe",
"intelliSenseMode": "gcc-arm",
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
"configurationProvider": "ms-vscode.cpptools",
diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json
index f4df96592..5c46d3979 100644
--- a/.vscode/example/launch.json
+++ b/.vscode/example/launch.json
@@ -38,6 +38,7 @@
"postAttachCommands": [
// "compare-sections",
"source debug/flipperapps.py",
+ "fap-set-debug-elf-root build/latest/.extapps",
// "source debug/FreeRTOS/FreeRTOS.py",
// "svd_load debug/STM32WB55_CM4.svd"
]
@@ -59,6 +60,7 @@
"set confirm off",
"set mem inaccessible-by-default off",
"source debug/flipperapps.py",
+ "fap-set-debug-elf-root build/latest/.extapps",
// "compare-sections",
]
// "showDevDebugOutput": "raw",
@@ -76,9 +78,30 @@
"rtos": "FreeRTOS",
"postAttachCommands": [
"source debug/flipperapps.py",
+ "fap-set-debug-elf-root build/latest/.extapps",
]
// "showDevDebugOutput": "raw",
},
+ {
+ "name": "Attach FW (DAP)",
+ "cwd": "${workspaceFolder}",
+ "executable": "./build/latest/firmware.elf",
+ "request": "attach",
+ "type": "cortex-debug",
+ "servertype": "openocd",
+ "device": "cmsis-dap",
+ "svdFile": "./debug/STM32WB55_CM4.svd",
+ "rtos": "FreeRTOS",
+ "configFiles": [
+ "interface/cmsis-dap.cfg",
+ "./debug/stm32wbx.cfg",
+ ],
+ "postAttachCommands": [
+ "source debug/flipperapps.py",
+ "fap-set-debug-elf-root build/latest/.extapps",
+ ],
+ // "showDevDebugOutput": "raw",
+ },
{
"name": "fbt debug",
"type": "python",
diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json
index d84707e07..19a03b69d 100644
--- a/.vscode/example/settings.json
+++ b/.vscode/example/settings.json
@@ -6,13 +6,13 @@
"cortex-debug.enableTelemetry": false,
"cortex-debug.variableUseNaturalFormat": true,
"cortex-debug.showRTOS": true,
- "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin",
+ "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin",
"cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin",
"cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin",
- "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe",
+ "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/openocd/bin/openocd.exe",
"cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd",
"cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd",
- "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gdb-py.bat",
+ "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gdb-py.bat",
"cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py",
"cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py",
"editor.formatOnSave": true,
diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json
index 3946d05cc..9baaf97b4 100644
--- a/.vscode/example/tasks.json
+++ b/.vscode/example/tasks.json
@@ -109,13 +109,13 @@
"label": "[Debug] Build FAPs",
"group": "build",
"type": "shell",
- "command": "./fbt plugin_dist"
+ "command": "./fbt fap_dist"
},
{
"label": "[Release] Build FAPs",
"group": "build",
"type": "shell",
- "command": "./fbt COMPACT=1 DEBUG=0 plugin_dist"
+ "command": "./fbt COMPACT=1 DEBUG=0 fap_dist"
},
{
"label": "[Debug] Launch App on Flipper",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75d414b30..39d7c1e71 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,18 +1,42 @@
### New changes
-* Add 433Mhz Security+ 1.0/2.0 in add manually in subghz
-* Update Minesweeper (https://github.com/panki27/minesweeper)
------
-* SubGHz: Fix DTM Neo (keeloq) encoder
-* fbt: fix flash usb without resources
-* OFW: DesktopSettings: reset submenu before running dialog
+* Plugins: SubGHz Bruteforcer -> Add support for Ansonic 12bit protocol (FM238)
+* Plugins: Fix DTMF Dolphin -> Add forgotten scene and menu item
+* Plugins: Update DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin)
+* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
+* Plugins: iButton Fuzzer and RFID Fuzzer improvements
+* Plugins: i2c tools fix name display
+* Plugins: Add 3 new plugins BlackJack, Solitaire [(by teeebor)](https://github.com/teeebor/flipper_games) and HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipperzero-firmware)
+* Plugins -> PR: Wifi marauder BT menus option (by @rf-bandit | PR #164)
+* Plugins -> PR: Update i2c tools (New UI) (by @NaejEL | PR #171)
+* Plugins -> PR: Fix htu21d falsely reading temp as humidity (by @GottZ | PR #175)
+* SubGHz -> PR: GUI Fix - Allow setting RSSI trigger to beggining (by @TQMatvey | PR #180)
+* SubGHz: Remove not widely used frequency from hopper
+* SubGHz: Fix starline encoder
+* SubGHz: Frequency Analyzer -> Save last trigger level
+* SubGHz: Speedup subghz launch from favourites
+* SubGHz: Add new freqs and modulation to user config
+* Infrared: Update universal remote assets (by @Amec0e)
+* CI/CD: Improvements, dev builds (can be found in this telegram channel -> https://t.me/kotnehleb)
+* Power -> PR: Show battery percentile while charging (by @TQMatvey | PR #178)
+* Docs -> PR: Some updates (by @lucemans | PR #169 and #170)
+* CLI -> PR: Update cli_commands.c To add `src` / `source` command for people exploring cli (by @PharoahCoder | PR #176)
+* OFW: Fix U2F HID vulnerability
+* OFW: Core: thread allocation shortcut
+* OFW: WS: add protocol GT-WT02
+* OFW: SubGhz: add protocol "Ansonic"
+* OFW: SubGhz: add protocol Nice_Flo 20bit
-#### [π² Download extra apps pack](https://download-directory.github.io/?url=https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed)
+#### [π² Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
[-> Download qFlipper 1.2.1 (allows .tgz installation) (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.1/)
## Please support development of the project
+* Boosty: https://boosty.to/mmxdev
+* destream (100 EUR min): https://destream.net/live/MMX/donate
+* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`
+* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
@@ -23,10 +47,3 @@
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for iOS mobile app / qFlipper
Update using qFlipper (1.2.0+) is now possible with `.tgz` update package! Also you can use Web Updater or self-update package.
-
-
-## Support unleashed (Not RM) so unleashed team can develop new features
-* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
-* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
-* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
-* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
diff --git a/RM11240405-0.71.2-f50f05b.tgz b/RM11240405-0.71.2-f50f05b.tgz
new file mode 100644
index 000000000..3921339a6
Binary files /dev/null and b/RM11240405-0.71.2-f50f05b.tgz differ
diff --git a/RM11240405-0.71.2-f50f05b.zip b/RM11240405-0.71.2-f50f05b.zip
new file mode 100644
index 000000000..0c656f4d8
Binary files /dev/null and b/RM11240405-0.71.2-f50f05b.zip differ
diff --git a/ReadMe.md b/ReadMe.md
index 4b294d876..1e9fd9c78 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -1,51 +1,52 @@
-
+# Flipper Zero FW [ROGUEMASTER]
+This firmware is a fork of [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) and the main Flipper Devices FW! I will try to keep active development and updates from both in this build along with any other projects that can be found to be useful to the community. I try to keep this FW build the most cutting edge with updates from both and updates from active community projects.
-[Join THE Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe) & [support us if you like what you see](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)! πππΈ
-|
+This software is for experimental purposes only and is not meant for any illegal activity/purposes. We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.
-#### Thank you to all the supporters; this firmware is a fork of [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) & the main Flipper Devices FW! I will try to keep active development and updates from both in this build along with any other projects that can be found to be useful to the community. I try to keep this FW build the most cutting edge with updates from both and updates from active community projects. All features and projects pulled are listed in expandable sections below. Please do [support us](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md) and/or [xMasterX](https://github.com/DarkFlippers/unleashed-firmware)! Everyone gives much of their uncompensated free time to ensure the success of the Flipper Zero!
+## [Support Us!](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)
+Everyone gives much of their uncompensated free time to ensure the success of the Flipper Zero!
+Thank you to all the supporters!
- |  |
+- [Support us if you like what you see](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)! πππΈ
+- Donations: BTC: `3MPQbKmGRCstg4FjnadfHa3woCT94JkR2a`
+- Donations: ETH: `0xC32Ea488DBeCF95992A5C81BD411e56Bd418BC5f`
+- [Join THE Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe)
-Latest Updates:
+## Latest Updates
-- To avoid Application errors and duplicates, delete /ext/apps before doing the RM firmware update
- Known Issues: `Chess`
-- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-10-13 17:56 GMT`
-- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-10-13 17:56 GMT`
-- Updated [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin)
-- Assets: Includes New Dolphin Animations: [Blaster (By Sasquach)]
-- Assets: Includes New Dolphin Animations: [Flipper City (By Svaarich)]
-- Updated Random Deed Function To Include New Options from OFW
-- Updated Assets: Dolphin Animations: [Rogue Master Custom Firmware (By Sasquach)] & [Narut0 (By Sasquach)]
-- OFW: Dolphin score points update **(WARNING!!! Dolphin Level will be reset after installing of this release!!!!)**
-- Updated view for desktop to exclude icons on the left and show minimal battery [By Talking-Sasquach](https://github.com/skizzophrenic/Talking-Sasquach/tree/main/Battery%20Only%20Top%20Status%20Bar)
-- Added: [Black Jack (By teeebor)](https://github.com/teeebor/flipper_games) (Thank you Haseo for PR)
-- Added: [Music Beeper (By OFW)](With Changes By qqMajiKpp/Haseo)
-- Chip 8 Removed, Tama Fix and Tanks Fixed [By Haseo](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/362)
+- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-11-23 21:01 EST`
+- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-11-23 21:01 EST`
+- Added: [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker)
+- Updated: [DHT Temp Monitor (By quen0n)](https://github.com/quen0n/FipperZero-DHT-Monitor) `Req: DHT11/DHT22(AM2302)/AM2301`
+- Updated: [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/derskythe/flipperzero-subbrute/tree/master)
+- Updated: [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC)
+- Updated: [Authenticator/TOTP (By akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
+- Updated: [Temperature Sensor (By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) `Req: HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320`
+- Updated: [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker)
+- Added: [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen)
+- Updated: [Timelapse (By theageoflove)](https://github.com/theageoflove/flipperzero-zeitraffer)
+- Added: [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan)
-
- TO DO / REMOVED
+## Install from Release
+FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE!
+- To avoid Application errors, delete /ext/apps before doing the RM firmware update
+- To install new FW, extract the [latest release zip file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/releases) to a folder, put the folder in the update folder on your SD card, and run the update file inside the folder using the Archive app (down from flipper desktop). If you were previously unleashed, you need to update your extend_range.txt file.`UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!` On any update, you may need to update this file and your unirf map file, so keep backups. π
-- [Keynote BT plugin: long press on OK to switch between Space and Retur⦠#1729 (By coded-with-claws)]
-- GPIO: Feature to read EEPROM of SFP Modules using I2C [(By marcusju)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/198)
-- Settings: Rename from App [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259)
-- Lost To Faps: Settings: Favorite Game by holding UP on Desktop [Thanks to gotnull](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/57)
-- Lost To Faps: Settings: Hold Down for Games Menu [(Thanks to ESurge)](https://github.com/ESurge/flipperzero-firmware-wPlugins)
-- Moved SubGHz Remote to FAP App (Not Working, So Reverted To Internal)
-- Moved GPIO to FAP App (Not Working, So Reverted To Internal)
-- Moved Infrared to FAP App (Not Working, So Reverted To Internal)
-- [IΒ²C-Scanner #1431 (By GitChris3004)](https://github.com/flipperdevices/flipperzero-firmware/pull/1431)
+## Build
+```bash
+$ git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git
+$ cd flipperzero-firmware-wPlugins/
+$ ./fbt updater_package
-
+# If building FAPS:
+$ ./fbt fap_dist
-
- This software is for experimental purposes only and is not meant for any illegal activity/purposes. We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. |
-
- FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE
+# If building image assets:
+$ ./fbt resources icons dolphin_ext
+```
-
- HOW TOs
+## How To
- [HERE IS A GUIDE FOR INSTALL (BY PINGYWON)](https://flipper.pingywon.com/)
- [HERE IS A NOOB GUIDE TO FLASH AND UNLOCK (BY interestingsoup)](https://interestingsoup.com/n00b-guide-flashing-flipper-zero-to-rougemaster/)
@@ -59,62 +60,43 @@
- [How to add extra SubGHz frequencies](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/documentation/SubGHzSettings.md)
- [π Extra plugins precompiled for Unleashed](https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed)
- [Configure Sub-GHz Remote App](https://github.com/RogueMaster/flipperzero-firmware/blob/420/documentation/SubGHzRemotePlugin.md)
-
+- [GAMES ONLY MODE PASSWORD: UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT IN CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)
+- [FLIPPER PROJECT WISH LIST](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md)
+- [SAMPLE EDUCATIONAL PROJECTS π](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md)
+- Contact me on [Discord](https://discord.gg/gF2bBUzAFe) if you want a renamed Flipper DFU. I can do custom names too!
+- [`FUN Links HERE` Collection for your Flipper SD](https://github.com/RogueMaster/awesome-flipperzero-withModules)
-- To avoid Application errors, delete /ext/apps before doing the RM firmware update
-- To install new FW, extract the [latest release zip file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/releases) to a folder, put the folder in the update folder on your SD card, and run the update file inside the folder using the Archive app (down from flipper desktop). If you were previously unleashed, you need to update your extend_range.txt file. **UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!** On any update, you may need to update this file and your unirf map file, so keep backups. π
- - [GAMES ONLY MODE PASSWORD: UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT IN CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)
- [FLIPPER PROJECT WISH LIST](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md) - [SAMPLE EDUCATIONAL PROJECTS π](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md)
- [`FUN Links HERE` Collection for your Flipper SD](https://github.com/RogueMaster/awesome-flipperzero-withModules)
- |
-DONATIONS ACCEPTED πππΈ
-BTC: 3MPQbKmGRCstg4FjnadfHa3woCT94JkR2a
-ETH: 0xC32Ea488DBeCF95992A5C81BD411e56Bd418BC5f
-
-
-Contact me on [Discord](https://discord.gg/gF2bBUzAFe) if you want a renamed Flipper DFU. I can do custom names too!
-
[Join my Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe)
-
- You should clone with
+## All Changes/Features
- ```shell
-$ git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git
-$ cd flipperzero-firmware-wPlugins/
-$ ./fbt resources icons
-$ ./fbt updater_package
-
-# If building FAPS:
-$ ./fbt plugin_dist
-```
-
- |
-
-
-
- All Changes/Features
-
+- Animations: File `/ext/dolphin/manifest.txt` no longer will get overwritten. Any automatic building of animations is disabled [here](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/fbe9175e0c828a54e651ee11f64f10f21e36a907).
- Animations: Hold Center to change Flipper idle animation. [Thanks to Zycenios](https://github.com/flipperdevices/flipperzero-firmware/commit/111786ef40e50a40d2e510595672b569d9b97bba) With changes by RogueMaster.
- Animations: iButton and RFID P0kem0n images [Thanks to Panzer00Z](https://github.com/Panzer00Z/flipperzero-firmware/)
- Animations: Idle animations will show all animations regardless of level and butthurt [Thanks to qqMajiKpp]
+- Animations: Painting speech bubbles updated from FalsePhilosopher
- Animations: RM FW Update image [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/257)
- Animations: SubGHZ Scanning image with Pikachu [Thanks to Panzer00Z](https://github.com/Panzer00Z/flipperzero-firmware/blob/3a548ea9bb181c9348d8afb427890c411456134e/assets/icons/SubGhz/Scanning_123x52.png)
+- Animations: Trimmed out the Flipper animations. `/ext/dolphin` folder on your Flipper should now be managed by you! [Copy this folder (RM Select)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-RMselect) or [this folder (RM minimal)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-minimal) if you don't want to do the work but want more animations.
+- [Archive: File Browser Ordering (By Dig03)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/389)
+- [Archive: Browser: Context menu to show file content (By askoriy)](https://github.com/DarkFlippers/unleashed-firmware/pull/139)
- Archive: FAPs are now launchable from Archive [By RogueMaster], thanks xMasterX for the suggestion
-- Assets: Includes a NFC Level 50 Gan0n Amiibo
+- Assets: Includes a NFC Level 50 Z3lda Amiibo
- Assets: Includes a NFC Rick Roll link
- Assets: Includes New Dolphin Animations: [Listed Here](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/assets/resources/dolphin)
- Assets: Includes sample Music Player tunes
- Assets: Includes sample SubGHz assets for Crosswalk, Handicap Doors, Sextoys, Tesla Charge Port, and Unitree Go1 Robot Dog
- Assets: Includes Sonic Screw Driver sound for the Wav Player
- Assets: Running DolphinRestorer.fap on new install will auto-level to Level 7.
+- [BadUSB: BadUSB as FAP #396 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/396)
- BadUSB: Added ignore DUCKY_LANG cmd to retain compatibility with existing scripts [(Thanks to v1nc)](https://github.com/v1nc/flipperzero-firmware)
-- BadUSB: Assets for Kiosk Evasion and Wifi Stealer
+- BadUSB: Assets for Kiosk Evasion (By nocomp) and Wifi Stealer (By 7h30th3r0n3)
- BadUSB: [Dummy decoy/bad usb keyboard layout #1525 (By dummy-decoy)](https://github.com/flipperdevices/flipperzero-firmware/pull/1525)
- BadUSB: nb-NO Added norwegian keyboard layout [(By jd-raymaker)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/357)
- BadUSB: show script errors on screen [(By CromFr)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/200)
- BadUSB: sk-SK maping keybord for BadUsb [(By jaroslavmraz)](https://github.com/flipperdevices/flipperzero-firmware/pull/1619)
-- Clock.fap, iButton.fap and U2F.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336)
+- Clock: Clock.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336)
- Desktop: Hidden top bar [Thanks to ESurge](https://github.com/ESurge/)
+- Desktop: Exclude icons on the left and show minimal battery [Thanks to skizzophrenic/Talking-Sasquach](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/360)
- Development free space thanks to removal of unused debug tools and [thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/46/files) for removal of first start assets.
- Dolphin: Assigned profile pic for levels 1-10 (Happy Lvl 1 Dolphin), 11-15 (Happy Lvl 2 Dolphin), 16-18 (Happy Lvl 3 Dolphin), 19-21 (Kid G0ku), 22-24 (Adult G0ku), 25-27 (SSJ G0ku) and 28-30 (SSJ3 G0ku)
- Dolphin: Expanded max level from 3 to 30 using [Roll20](https://roll20.net/compendium/dnd5e/Monsters#h-Experience%20Points), Increased max deed XP per action type from 15 to 45 exp daily & updated animation manifest for max level 30 for all animations (By RogueMaster)
@@ -125,29 +107,37 @@ $ ./fbt plugin_dist
- Dolphin: Passport: Show EXP [(By Dabolus)](https://github.com/Dabolus/flipperzero-firmware-rpg/)
- Dolphin: Changed daily MAX to 198 on all 7 Deed Types. Random Deed Selection used for MAX +3 EXP daily gain.
- Dolphin: Plugin Achivement +3 EXP for a total of up to 700 EXP daily. Plugin Achivements are:
-- - Dice First Roll for d20+ = sides on dice (i.e. Nat 20 on d20)
-- - Dice First Roll for d20+ = sides on dice - 1 (+1 EXP) (i.e. 19 on d20)
-- - Games Only Mode from Lock Menu
-- - Getting 2048 in 2048
-- - Mouse Jacker Ducky Run
-- - NRFSniffer Found Address
-- - Stopwatch @ Alert
-- - Tetris, Snake, or Flappy Bird Score For EXP
-- - Zombiez for every 20 Zombie kills
+ - Dice First Roll for d20+ = sides on dice (i.e. Nat 20 on d20)
+ - Dice First Roll for d20+ = sides on dice - 1 (+1 EXP) (i.e. 19 on d20)
+ - Games Only Mode from Lock Menu
+ - Getting 2048 in 2048
+ - Mouse Jacker Ducky Run
+ - NRFSniffer Found Address
+ - Stopwatch @ Alert
+ - Tetris, Snake, or Flappy Bird Score For EXP
+ - Zombiez for every 20 Zombie kills
- Dolphin: SD dolphin manifest updated to weight animations differently
+- GPIO: Feature to read EEPROM of SFP Modules using I2C [(By marcusju)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/198)
+- GPIO: [IΒ²C-Scanner #1431 (By GitChris3004)](https://github.com/flipperdevices/flipperzero-firmware/pull/1431)
+- iButton: [Fixed issue when loading iButton keys or U2F token from Archive app #382 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/382)
+- iButton: iButton.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336)
- Icon Decode/Encode [(Thanks to PixlEmly)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/55/files)
-- IR: Universal AC, Audio, Fans & Projectors from [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware)
+- [Infrared: Infrared AS FAP #398 (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/398) (With Thanks to ESurge) [Restored Infrared and RFID CLI #405 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/405)
+- Infrared: Universal AC, Audio, Fans & Projectors from [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware)
- Plugins: 2048, Arkanoid, Snake, and Tetris show score. Thanks to [whoamins](https://github.com/flipperdevices/flipperzero-firmware/commit/7feda832ede1ba8468eff2ca055fef3ddbdc16ac) and [DevMilanIan](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/188) With position changes by RogueMaster. Also all + Tic Tac Toe updated by [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware) for stability.
- Plugins: Icon for Clock [Thanks to Redlink](https://github.com/redlink2/flipperzero-firmware/tree/menuChanges)
+- [RFID: LFRFID AS FAP #397 (By ROgueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/397) (With Thanks to ESurge) [Restored Infrared and RFID CLI #405 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/405)
- Settings: "Lock W PIN + Off" add to UP menu [(By RogueMaster)]
- Settings: Actual PIN Lock [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/desktop/desktop.c)
- Settings: Auto-Lock Options Added: 10s+15s+90s [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c)
- Settings: Battery Meter on Desktop [Thanks to McAzzaMan](https://github.com/McAzzaMan/flipperzero-firmware/tree/BatteryPercentageView)
- Settings: Custom name with this compile: CUSTOM_FLIPPER_NAME=name ./fbt updater_package [By Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware)
- Settings: Desktop => [Games Only Mode (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)
-- - [UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT FROM CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)<== FULL LIST OF GAMES ONLY CONTROLS
+- - [UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT FROM CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) (FULL LIST OF GAMES ONLY CONTROLS)
- Settings: LCD Timeout Options Added: 10s+90s+2min+5min+10min [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/notification/notification_settings_app.c)
+- Settings: Rename from App [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/409)
- Settings: Rename from SD `dolphin/name.txt` [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259)
+- Settings: Power: Fix for settings reload every second [(By lokiuox)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/364)
- Settings: Scan names will have timestamp instead of random name assigned for [NFC](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/lib/toolbox/random_name.c) and [SubGHz](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/subghz/scenes/subghz_scene_read_raw.c) (By RogueMaster)
- Settings: Storage Info: [SD info: Add dynamic units and free % #1634 (By non-bin)](https://github.com/flipperdevices/flipperzero-firmware/pull/1634)
- Settings: Updated Dummy Mode mode to have access to 2048, Dice, Snake, Tetris & Zombiez [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/bf964fffdd2c1d730623673987a6de32a3f7c92f)
@@ -160,83 +150,105 @@ $ ./fbt plugin_dist
- SubGHz: [Add settings to subghz read functionality to allow setting RSSI threshold (raw only) (By PolymerPrints)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/184)
- SubGHz: Extended ranges enabled through flag in /ext/subghz/assets/extend_range.txt [from tkerrby](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/116)
- SubGHz: Moved setting_user file to setting_user.txt! This makes it changable from IOS app. (By RogueMaster)
-- SubGHz: New frequency analyzer [(By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1501) [feedback mode (by darmiel)](https://github.com/darmiel/flipper-playlist/tree/feat/stealth-frequency-analyzer) [Quiet Mode (by Himura2la)](https://github.com/ClusterM/flipperzero-firmware/pull/1)
+- SubGHz: New frequency analyzer [(By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1501) [feedback mode (by darmiel)](https://github.com/darmiel/flipper-playlist/tree/feat/stealth-frequency-analyzer) [Quiet Mode (by Himura2la)](https://github.com/ClusterM/flipperzero-firmware/pull/1) [New frequency analyzer #1557 (By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1557)
- SubGHz: Protocols An-Motors, BFT Mitto, Came Atomo, FAAC SLH (Spa), HCS101, Keeloq, Keeloq Common, Nice Flor S, SecPlus v1+v2 and Star Line updates from [Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware)
- SubGHz: Unlock from SD flag from [(cloudbreakdaniel)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/97db0dc91ee3dff812b4dec0618e3f198de14405). Update `subghz/assets/extend_range.txt` with [this file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/assets/resources/subghz/assets/extend_range.txt) on SD. **UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!**
+- U2F: U2F.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336)
-
-
-
-
- Open PRs Checked Out & Not Merged In Main
-
-- [NFC: Display UL PWD_AUTH payload / ntag-pwd-capture #1471 (Thanks to GMMan)](https://github.com/flipperdevices/flipperzero-firmware/pull/1471)
-- [New frequency analyzer #1557 (By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1557)
-- [Automatic shutdown on idle #1647 (By SHxKenzuto)](https://github.com/flipperdevices/flipperzero-firmware/pull/1647)
-- [Decode RAW recordings #1667 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1667)
-
-
-
- |
-
-
- GAMES
+## Games
+- [15 (By x27)](https://github.com/x27/flipperzero-game15)
- [2048 (By OlegSchwann)](https://github.com/OlegSchwann/flipperzero-firmware/tree/hackaton/game_2048/applications/game-2048) [(Score By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/186)
- [Arkanoid (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins) [(Score By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/188)
-- [Black Jack (By teeebor)](https://github.com/teeebor/flipper_games)
+- [BlackJack (By teeebor)](https://github.com/teeebor/flipper_games)
- [Chess (By Okalachev)](https://github.com/okalachev/flipperzero-firmware/tree/chess) Crashes 1st load if FW <~750KB or every load on larger FW `Broken?`
-- [Chip8 Emulator (By mega8bit)](https://github.com/mega8bit/flipperzero-firmware) Updated by ESurge. Add SD folder `chip8`, [Get GAMES HERE](https://johnearnest.github.io/chip8Archive/) (Needs Controls Programmed) `HIDDEN because its broken`
-- [Doom (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
- [Dice Roller Including SEX/WAR/8BALL/WEED DICE (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/dice/dice.c)
-- [Flappy Bird (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
+- [Doom (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
+- [Flappy Bird (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird) [Flappy: Border hitboxes, bigger Pilars (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/114) [Increase pilars line width to improve visibility (By ahumeniy)](https://github.com/DarkFlippers/unleashed-firmware/pull/140)
- [Game of Life (Updated to work by tgxn) (By itsyourbedtime)](https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c)
+- [Heap Defence (By xMasterX)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/fc776446de9fdd553b221c02668b925b689378d8) [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki)
- [Mandelbrot Set (By Possibly-Matt)](https://github.com/Possibly-Matt/flipperzero-firmware-wPlugins)
- [Minesweeper (By panki27)](https://github.com/panki27/minesweeper)
- [Monty Hall (By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/203)
-- Snake [OFW]
+- [Scorched Tanks (By jasniec)](https://github.com/jasniec/flipper-scorched-tanks-game)
+- [Snake (By OlegSchwann)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/829)(With updates from DrZlo13, xMasterX, QtRoS and RogueMaster) [Snake Score Saving (By JuanJakobo)](https://github.com/flipperdevices/flipperzero-firmware/pull/1922) [Turns anywhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/125) [Food Spawns Anywwhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/130)
+- [Solitaire (By teeebor)](https://github.com/teeebor/flipper_games)
+- [T-Rex (By gelin)](https://github.com/gelin/t-rex-runner) WIP
- [TAMA P1 (By GMMan)](https://github.com/GMMan/flipperzero-firmware/tree/tama-p1) requires [this rom](https://tinyurl.com/tamap1) IN `tama_p1` on SD as `rom.bin` to make it work.
- [Tanks (By Alexgr13)](https://github.com/alexgr13/flipperzero-firmware/tree/fork/dev/applications/tanks-game)
- [Tetris (By jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
- [Tic Tac Toe (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
- [Video Poker (By PixlEmly)](https://github.com/PixlEmly/flipperzero-firmware-testing/blob/420/applications/VideoPoker/poker.c)
- [Zombiez (Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez)
-
-
- PLUGINS
+## Plugins
-- [Bluetooth Remote (By Cutch)[OFW]](https://github.com/flipperdevices/flipperzero-firmware/pull/1330)
+- [Air Mouse (By ginkage)](https://github.com/ginkage/FlippAirMouse/)
+- [Authenticator/TOTP (By akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
+- [Bad Apple (By GMMan)](https://github.com/GMMan/flipperzero-badapple) `video.bin NEEDED, Please LMK if you have it`
+- [Barcode Generator (By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
+- [Bluetooth Remote (By Cutch)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1330)
- [BPM Tapper (By panki27)](https://github.com/panki27/bpm-tapper)
- [Calculator (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e)
- [Ceasar Cipher (By panki27)](https://github.com/panki27/caesar-cipher)
- [Clock/Stopwatch (By CompaqDisc, Stopwatch & Sound Alert By RogueMaster)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) [12/24HR (By non-bin)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/254) [Refactoring (By GMMan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/256)
-- [DSTIKE Deauther (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module/tree/FlipperZero-Module-v2/FlipperZeroModule/FlipperZero-ESP8266-Deauth-App)) `Req: ESP8266`
-- [Dolphin Backup (By nminaylov)](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Modified by RogueMaster
+- [Counter (By Krulknul)](https://github.com/Krulknul/dolphin-counter)
+- [DAP Link (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1897)
+- [Deauther PWNDTOOLS V2.6.0 (By HEX0DAYS)](https://github.com/HEX0DAYS/FlipperZero-PWNDTOOLS) `Req: ESP8266` [Original](https://github.com/SpacehuhnTech/esp8266_deauther)
+- [DHT Temp Monitor (By quen0n)](https://github.com/quen0n/FipperZero-DHT-Monitor) `Req: DHT11/DHT22(AM2302)/AM2301`
+- [Distance Sensor (By Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04)) `Req: HC-SR04` Ported/Modified by xMasterX
+- [Dolphin Backup (By nminaylov)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Modified by RogueMaster
- [Dolphin Restorer (By nminaylov)](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Cloned by RogueMaster
+- [DSTIKE Deauther (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module/tree/FlipperZero-Module-v2/FlipperZeroModule/FlipperZero-ESP8266-Deauth-App) `Req: ESP8266`
- [DTMF Dolphin (By litui)](https://github.com/litui/dtmf_dolphin)
+- [Flashlight (By xMasterX)](https://github.com/xMasterX/flipper-flashlight)
+- [GPS (By ezod)](https://github.com/ezod/flipperzero-gps) `Req: NMEA 0183`
+- [HEX Viewer (By QtRoS)](https://github.com/QtRoS/flipperzero-firmware)
+- [iButton Fuzzer (By xMasterX)](https://github.com/DarkFlippers/unleashed-firmware)
+- [i2c Tools (By NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools)
+- [Lightmeter (By oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter) `Req: BH1750`
+- [IFTTT Virtual Button (By Ferrazzi)](https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button) `Req: ESP8266 w/ IFTTT FW Flashed`
- [Metronome (By panki27)](https://github.com/panki27/Metronome)
+- [Morse Code (By wh00hw)](https://github.com/DarkFlippers/unleashed-firmware/pull/144)
- [Mouse Jacker (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/mousejacker) from nocomp/Frog/UberGuidoZ) `Req: NRF24`
- [Mouse Jiggler (By Jacob-Tate)](https://github.com/Jacob-Tate/flipperzero-firmware/blob/dev/applications/mouse_jiggler/mouse_jiggler.c) (Original By MuddleBox)
- [Multi Converter (By theisolinearchip)](https://github.com/theisolinearchip)
-- Music Beeper [OFW] (Music Player With Changes By qqMajiKpp/Haseo)
-- Music Player [OFW]
+- [Music Beeper (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/pull/1189) (With Changes By qqMajiKpp/Haseo)
+- [Music Player (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1189)
+- [NFC Magic (By gornekich)](https://github.com/flipperdevices/flipperzero-firmware/pull/1966)
- [NRF Sniff (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/nrfsniff) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/nrfsniff) from nocomp/Frog/UberGuidoZ) `Req: NRF24`
+- [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan)
+- [Ocarina (By invalidna-me)](https://github.com/invalidna-me/flipperzero-ocarina) [Here are the LOTZ Songs](https://www.zeldadungeon.net/wiki/Ocarina_of_Time_Songs)
- [Paint (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e)
+- [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen)
- [PicoPass Reader (By Bettse)](https://github.com/flipperdevices/flipperzero-firmware/pull/1366)
+- [Pomodoro Timer (By sbrin)](https://github.com/sbrin/flipperzero_pomodoro)
- [RFID Fuzzer (By Ganapati)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/245) [Changes by Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware)
- [RF Remix (By ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix) [(Original By jimilinuxguy)](https://github.com/jimilinuxguy/flipperzero-universal-rf-remote/tree/028d615c83f059bb2c905530ddb3d4efbd3cbcae/applications/jukebox) [(More protocols thanks to darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/blob/feat/unirf-protocols/applications/unirfremix/unirfremix_app.c)
+- [SAM (By Unknown)][Original?](https://github.com/ctoth/SAM)
- [Sentry Safe (By H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/sentry_safe) from [UberGuidoZ](https://github.com/UberGuidoZ/))
+- [Signal Generator (By nminaylov)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1793)
- [Spectrum Analyzer (By jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) [Updates (for testing) Thanks to theY4Kman](https://github.com/theY4Kman/flipperzero-firmware)
-- [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57)
+- [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/derskythe/flipperzero-subbrute/tree/master)
- [Sub-GHz Playlist (By darmiel)](https://github.com/darmiel/flipper-playlist)
-- [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin)
-- [UPC-A Generator (By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
+- [Temperature Sensor (By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) `Req: HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320`
+- [Temperature Sensor (By xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/commit/9c4612e571db42f5e6123a3f159e01337453a6af) `Req: AM2320`
+- [Timelapse (By theageoflove)](https://github.com/theageoflove/flipperzero-zeitraffer)
+- [Tuning Fork (By besya)](https://github.com/besya/flipperzero-tuning-fork)
+- [UART Echo (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/831)
+- [USB HID Autofire (By pbek)](https://github.com/pbek/usb_hid_autofire)
- [USB Keyboard (By huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
-- [WAV Player (By Zlo)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work
-- [WiFi (Deauther) (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2)
-- [WiFi (Marauder) (By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `REQUIRES ESP32 WITH MARAUDER FLASHED`
+- [WAV Player (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work
+- [WiFi (Deauther) V2 (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2) `Req: ESP8266`
+- [WiFi (Marauder) (By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `Req: ESP32 WITH MARAUDER FLASHED`
- [WiFi Scanner v.0.4 (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module-ESP8266) `Req: ESP8266 or ESP32`
-
+- [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC)
+- [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker)
- |
+## Open PRs Checked Out & Not Merged In Main
+
+- [Automatic shutdown on idle #1647 (By SHxKenzuto)](https://github.com/flipperdevices/flipperzero-firmware/pull/1647)
+- [Decode RAW recordings #1667 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1667)
+- [NFC - Machine Readable Travel Documents #1866 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1866)
+- [nfc: NTAG password auto capture (and other password-related changes) #1843 (By GMMan)](https://github.com/flipperdevices/flipperzero-firmware/pull/1843)
+- [Snake Plugin: Store game state on close and restore it on restart, show highscore #1922 (By JuanJakobo)](https://github.com/flipperdevices/flipperzero-firmware/pull/1922)
diff --git a/RoadMap.md b/RoadMap.md
index 0cf95331d..8c9fcea8e 100644
--- a/RoadMap.md
+++ b/RoadMap.md
@@ -6,9 +6,9 @@
- I tried to organize them in terms of difficulty.
## WISH LIST ITEMS:
+- Battery Type moved to Power Settings
- `Notepad` APP to allow taking quick notes and saving to SD.
- - Also can possibly open/edit .md,.txt,.fmf and other text friendly formats
-- `UART` Move UART Echo app from Debug to GPIO section.
- `Write URL to NFC` APP to allow creating URL NFC tags from only the flipper
- - Also can possibly support larger URLs than the ones in samples (due to length limits on NFC types)
- - Also can possibly create other types of tags, like WIFI configurations
diff --git a/SConstruct b/SConstruct
index 74fa5667b..474175c14 100644
--- a/SConstruct
+++ b/SConstruct
@@ -7,6 +7,7 @@
# construction of certain targets behind command-line options.
import os
+from fbt.util import path_as_posix
DefaultEnvironment(tools=[])
@@ -33,10 +34,6 @@ coreenv = SConscript(
)
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
-# Store root dir in environment for certain tools
-coreenv["ROOT_DIR"] = Dir(".")
-
-
# Create a separate "dist" environment and add construction envs to it
distenv = coreenv.Clone(
tools=[
@@ -47,6 +44,7 @@ distenv = coreenv.Clone(
"jflash",
],
ENV=os.environ,
+ UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
)
firmware_env = distenv.AddFwProject(
@@ -144,23 +142,28 @@ distenv.Default(basic_dist)
dist_dir = distenv.GetProjetDirName()
fap_dist = [
distenv.Install(
- f"#/dist/{dist_dir}/apps/debug_elf",
- firmware_env["FW_EXTAPPS"]["debug"].values(),
+ distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
+ list(
+ app_artifact.debug
+ for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
+ ),
),
- *(
- distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1])
- for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
+ distenv.Install(
+ f"#/dist/{dist_dir}/apps",
+ "#/assets/resources/apps",
),
]
-Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
+Depends(
+ fap_dist,
+ list(
+ app_artifact.validator
+ for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
+ ),
+)
Alias("fap_dist", fap_dist)
# distenv.Default(fap_dist)
-plugin_resources_dist = list(
- distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
- for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
-)
-distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
+distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist)
# Target for bundling core2 package for qFlipper
@@ -198,6 +201,7 @@ firmware_debug = distenv.PhonyTarget(
source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
+ FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
)
distenv.Depends(firmware_debug, firmware_flash)
@@ -207,6 +211,7 @@ distenv.PhonyTarget(
source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
+ FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
)
# Debug alien elf
@@ -215,7 +220,7 @@ distenv.PhonyTarget(
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
- GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
+ GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
)
distenv.PhonyTarget(
@@ -235,14 +240,14 @@ distenv.PhonyTarget(
# Linter
distenv.PhonyTarget(
"lint",
- "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
- LINT_SOURCES=firmware_env["LINT_SOURCES"],
+ "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
+ LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
distenv.PhonyTarget(
"format",
- "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
- LINT_SOURCES=firmware_env["LINT_SOURCES"],
+ "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
+ LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
@@ -282,7 +287,7 @@ distenv.PhonyTarget(
)
# Start Flipper CLI via PySerial's miniterm
-distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
+distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
# Find blackmagic probe
@@ -291,6 +296,16 @@ distenv.PhonyTarget(
"@echo $( ${BLACKMAGIC_ADDR} $)",
)
+
+# Find STLink probe ids
+distenv.PhonyTarget(
+ "get_stlink",
+ distenv.Action(
+ lambda **kw: distenv.GetDevices(),
+ None,
+ ),
+)
+
# Prepare vscode environment
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
distenv.Precious(vscode_dist)
diff --git a/applications/debug/file_browser_test/application.fam b/applications/debug/file_browser_test/application.fam
index 5e4c7f467..4a401a649 100644
--- a/applications/debug/file_browser_test/application.fam
+++ b/applications/debug/file_browser_test/application.fam
@@ -8,4 +8,5 @@ App(
stack_size=2 * 1024,
order=150,
fap_category="Debug",
+ fap_icon_assets="icons",
)
diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c
index 5c7b93bcb..6cb50d385 100644
--- a/applications/debug/file_browser_test/file_browser_app.c
+++ b/applications/debug/file_browser_test/file_browser_app.c
@@ -1,4 +1,4 @@
-#include "assets_icons.h"
+#include
#include "file_browser_app_i.h"
#include "gui/modules/file_browser.h"
#include
diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badusb_10px.png
new file mode 100644
index 000000000..037474aa3
Binary files /dev/null and b/applications/debug/file_browser_test/icons/badusb_10px.png differ
diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c
index 701e5325a..16996ba8c 100644
--- a/applications/debug/uart_echo/uart_echo.c
+++ b/applications/debug/uart_echo/uart_echo.c
@@ -220,11 +220,7 @@ static UartEchoApp* uart_echo_app_alloc() {
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
- app->worker_thread = furi_thread_alloc();
- furi_thread_set_name(app->worker_thread, "UsbUartWorker");
- furi_thread_set_stack_size(app->worker_thread, 1024);
- furi_thread_set_context(app->worker_thread, app);
- furi_thread_set_callback(app->worker_thread, uart_echo_worker);
+ app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
furi_thread_start(app->worker_thread);
return app;
diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c
index 8879c8fc8..2bcb95da8 100644
--- a/applications/debug/unit_tests/infrared/infrared_test.c
+++ b/applications/debug/unit_tests/infrared/infrared_test.c
@@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) {
infrared_test_run_decoder(InfraredProtocolRC5, 5);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST(infrared_test_decoder_nec) {
@@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
infrared_test_run_encoder(InfraredProtocolRC6, 1);
}
+MU_TEST(infrared_test_decoder_kaseikyo) {
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
+ infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
+}
+
MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
@@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
+ infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST_SUITE(infrared_test) {
@@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) {
MU_RUN_TEST(infrared_test_decoder_nec);
MU_RUN_TEST(infrared_test_decoder_samsung32);
MU_RUN_TEST(infrared_test_decoder_necext1);
+ MU_RUN_TEST(infrared_test_decoder_kaseikyo);
MU_RUN_TEST(infrared_test_decoder_mixed);
MU_RUN_TEST(infrared_test_encoder_decoder_all);
}
diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c
index f149508b0..454c11c0f 100644
--- a/applications/debug/unit_tests/nfc/nfc_test.c
+++ b/applications/debug/unit_tests/nfc/nfc_test.c
@@ -5,6 +5,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -16,6 +18,8 @@
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
+#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc")
+#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc")
static const char* nfc_test_file_type = "Flipper NFC test";
static const uint32_t nfc_test_file_version = 1;
@@ -220,11 +224,272 @@ MU_TEST(mf_classic_dict_test) {
furi_string_free(temp_str);
}
+MU_TEST(mf_classic_dict_load_test) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ mu_assert(storage != NULL, "storage != NULL assert failed\r\n");
+
+ // Delete unit test dict file if exists
+ if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) {
+ mu_assert(
+ storage_simply_remove(storage, NFC_TEST_DICT_PATH),
+ "remove == true assert failed\r\n");
+ }
+
+ // Create unit test dict file
+ Stream* file_stream = file_stream_alloc(storage);
+ mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n");
+ mu_assert(
+ file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS),
+ "file_stream_open == true assert failed\r\n");
+
+ // Write unit test dict file
+ char key_str[] = "a0a1a2a3a4a5";
+ mu_assert(
+ stream_write_cstring(file_stream, key_str) == strlen(key_str),
+ "write == true assert failed\r\n");
+ // Close unit test dict file
+ mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
+
+ // Load unit test dict file
+ MfClassicDict* instance = NULL;
+ instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
+ mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
+ uint32_t total_keys = mf_classic_dict_get_total_keys(instance);
+ mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n");
+
+ // Read key
+ uint64_t key_ref = 0xa0a1a2a3a4a5;
+ uint64_t key_dut = 0;
+ FuriString* temp_str = furi_string_alloc();
+ mu_assert(
+ mf_classic_dict_get_next_key_str(instance, temp_str),
+ "get_next_key_str == true assert failed\r\n");
+ mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n");
+ mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
+ mu_assert(
+ mf_classic_dict_get_next_key(instance, &key_dut),
+ "get_next_key == true assert failed\r\n");
+ mu_assert(key_dut == key_ref, "invalid key loaded\r\n");
+ furi_string_free(temp_str);
+ mf_classic_dict_free(instance);
+
+ // Check that MfClassicDict added new line to the end of the file
+ mu_assert(
+ file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING),
+ "file_stream_open == true assert failed\r\n");
+ mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n");
+ uint8_t last_char = 0;
+ mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n");
+ mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n");
+ mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
+
+ // Delete unit test dict file
+ mu_assert(
+ storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n");
+ stream_free(file_stream);
+ furi_record_close(RECORD_STORAGE);
+}
+
+MU_TEST(nfca_file_test) {
+ NfcDevice* nfc = nfc_device_alloc();
+ mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n");
+ nfc->format = NfcDeviceSaveFormatUid;
+
+ // Fill the UID, sak, ATQA and type
+ uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00};
+ memcpy(nfc->dev_data.nfc_data.uid, uid, 7);
+ nfc->dev_data.nfc_data.uid_len = 7;
+
+ nfc->dev_data.nfc_data.sak = 0x08;
+ nfc->dev_data.nfc_data.atqa[0] = 0x00;
+ nfc->dev_data.nfc_data.atqa[1] = 0x04;
+ nfc->dev_data.nfc_data.type = FuriHalNfcTypeA;
+
+ // Save the NFC device data to the file
+ mu_assert(
+ nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n");
+ nfc_device_free(nfc);
+
+ // Load the NFC device data from the file
+ NfcDevice* nfc_validate = nfc_device_alloc();
+ mu_assert(
+ nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true),
+ "nfc_device_load == true assert failed\r\n");
+
+ // Check the UID, sak, ATQA and type
+ mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n");
+ mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n");
+ mu_assert(
+ nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n");
+ mu_assert(
+ nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n");
+ mu_assert(
+ nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
+ "type == FuriHalNfcTypeA assert failed\r\n");
+ nfc_device_free(nfc_validate);
+}
+
+static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
+ NfcDevice* nfc_dev = nfc_device_alloc();
+ mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n");
+ nfc_dev->format = NfcDeviceSaveFormatMifareClassic;
+
+ // Create a test file
+ nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type);
+
+ // Get the uid from generated MFC
+ uint8_t uid[7] = {0};
+ memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len);
+ uint8_t sak = nfc_dev->dev_data.nfc_data.sak;
+ uint8_t atqa[2] = {};
+ memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
+
+ MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
+ // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest])
+ uint8_t manufacturer_block[16] = {0};
+ memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
+ mu_assert(
+ memcmp(manufacturer_block, uid, uid_len) == 0,
+ "manufacturer_block uid doesn't match the file\r\n");
+ for(uint8_t i = uid_len; i < 16; i++) {
+ mu_assert(
+ manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
+ }
+
+ // Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6])
+ uint8_t sector_trailer[16] = {
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0x07,
+ 0x80,
+ 0x69,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF};
+ // Reference block data
+ uint8_t block_data[16] = {};
+ memset(block_data, 0xff, sizeof(block_data));
+ uint16_t total_blocks = mf_classic_get_total_block_num(type);
+ for(size_t i = 1; i < total_blocks; i++) {
+ if(mf_classic_is_sector_trailer(i)) {
+ mu_assert(
+ memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
+ "Failed sector trailer compare");
+ } else {
+ mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
+ }
+ }
+ // Save the NFC device data to the file
+ mu_assert(
+ nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH),
+ "nfc_device_save == true assert failed\r\n");
+ // Verify that key cache is saved
+ FuriString* key_cache_name = furi_string_alloc();
+ furi_string_set_str(key_cache_name, "/ext/nfc/cache/");
+ for(size_t i = 0; i < uid_len; i++) {
+ furi_string_cat_printf(key_cache_name, "%02X", uid[i]);
+ }
+ furi_string_cat_printf(key_cache_name, ".keys");
+ mu_assert(
+ storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) ==
+ FSE_OK,
+ "Key cache file save failed");
+ nfc_device_free(nfc_dev);
+
+ // Load the NFC device data from the file
+ NfcDevice* nfc_validate = nfc_device_alloc();
+ mu_assert(nfc_validate, "Nfc device alloc assert");
+ mu_assert(
+ nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false),
+ "nfc_device_load == true assert failed\r\n");
+
+ // Check the UID, sak, ATQA and type
+ mu_assert(
+ memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0,
+ "uid compare assert failed\r\n");
+ mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n");
+ mu_assert(
+ memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0,
+ "atqa compare assert failed\r\n");
+ mu_assert(
+ nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
+ "type == FuriHalNfcTypeA assert failed\r\n");
+
+ // Check the manufacturer block
+ mu_assert(
+ memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0,
+ "manufacturer_block assert failed\r\n");
+ // Check other blocks
+ for(size_t i = 1; i < total_blocks; i++) {
+ if(mf_classic_is_sector_trailer(i)) {
+ mu_assert(
+ memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
+ "Failed sector trailer compare");
+ } else {
+ mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
+ }
+ }
+ nfc_device_free(nfc_validate);
+
+ // Check saved key cache
+ NfcDevice* nfc_keys = nfc_device_alloc();
+ mu_assert(nfc_validate, "Nfc device alloc assert");
+ nfc_keys->dev_data.nfc_data.uid_len = uid_len;
+ memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len);
+ mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache");
+ uint8_t total_sec = mf_classic_get_total_sectors_num(type);
+ uint8_t default_key[6] = {};
+ memset(default_key, 0xff, 6);
+ for(size_t i = 0; i < total_sec; i++) {
+ MfClassicSectorTrailer* sec_tr =
+ mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i);
+ mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare");
+ mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare");
+ }
+
+ // Delete key cache file
+ mu_assert(
+ storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK,
+ "Failed to remove key cache file");
+ furi_string_free(key_cache_name);
+ nfc_device_free(nfc_keys);
+}
+
+MU_TEST(mf_classic_1k_4b_file_test) {
+ mf_classic_generator_test(4, MfClassicType1k);
+}
+
+MU_TEST(mf_classic_4k_4b_file_test) {
+ mf_classic_generator_test(4, MfClassicType4k);
+}
+
+MU_TEST(mf_classic_1k_7b_file_test) {
+ mf_classic_generator_test(7, MfClassicType1k);
+}
+
+MU_TEST(mf_classic_4k_7b_file_test) {
+ mf_classic_generator_test(7, MfClassicType4k);
+}
+
MU_TEST_SUITE(nfc) {
nfc_test_alloc();
+ MU_RUN_TEST(nfca_file_test);
+ MU_RUN_TEST(mf_classic_1k_4b_file_test);
+ MU_RUN_TEST(mf_classic_4k_4b_file_test);
+ MU_RUN_TEST(mf_classic_1k_7b_file_test);
+ MU_RUN_TEST(mf_classic_4k_7b_file_test);
MU_RUN_TEST(nfc_digital_signal_test);
MU_RUN_TEST(mf_classic_dict_test);
+ MU_RUN_TEST(mf_classic_dict_load_test);
nfc_test_free();
}
diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c
index 7c1c669ff..115009701 100644
--- a/applications/debug/unit_tests/storage/storage_test.c
+++ b/applications/debug/unit_tests/storage/storage_test.c
@@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) {
File* file = storage_file_alloc(storage);
// file_locker thread start
- FuriThread* locker_thread = furi_thread_alloc();
- furi_thread_set_name(locker_thread, "StorageFileLocker");
- furi_thread_set_stack_size(locker_thread, 2048);
- furi_thread_set_context(locker_thread, semaphore);
- furi_thread_set_callback(locker_thread, storage_file_locker);
+ FuriThread* locker_thread =
+ furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore);
furi_thread_start(locker_thread);
// wait for file lock
@@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) {
File* file = storage_file_alloc(storage);
// file_locker thread start
- FuriThread* locker_thread = furi_thread_alloc();
- furi_thread_set_name(locker_thread, "StorageDirLocker");
- furi_thread_set_stack_size(locker_thread, 2048);
- furi_thread_set_context(locker_thread, semaphore);
- furi_thread_set_callback(locker_thread, storage_dir_locker);
+ FuriThread* locker_thread =
+ furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore);
furi_thread_start(locker_thread);
// wait for dir lock
diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
index 210d3770f..3052448b7 100644
--- a/applications/debug/unit_tests/subghz/subghz_test.c
+++ b/applications/debug/unit_tests/subghz/subghz_test.c
@@ -5,7 +5,7 @@
#include
#include
#include
-#include
+#include
#include
#define TAG "SubGhz TEST"
@@ -13,7 +13,7 @@
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
-#define TEST_RANDOM_COUNT_PARSE 233
+#define TEST_RANDOM_COUNT_PARSE 244
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
@@ -43,6 +43,8 @@ static void subghz_test_init(void) {
environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment_handler, NICE_FLOR_S_DIR_NAME);
+ subghz_environment_set_protocol_registry(
+ environment_handler, (void*)&subghz_protocol_registry);
receiver_handler = subghz_receiver_alloc_init(environment_handler);
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
@@ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) {
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
}
-MU_TEST(subghz_decoder_magellen_test) {
+MU_TEST(subghz_decoder_magellan_test) {
mu_assert(
subghz_decoder_test(
- EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
- "Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
+ EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
}
MU_TEST(subghz_decoder_intertechno_v3_test) {
@@ -435,11 +437,11 @@ MU_TEST(subghz_decoder_clemsa_test) {
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
-MU_TEST(subghz_decoder_oregon2_test) {
+MU_TEST(subghz_decoder_ansonic_test) {
mu_assert(
subghz_decoder_test(
- EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME),
- "Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n");
+ EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
}
//test encoders
@@ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) {
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
}
-MU_TEST(subghz_encoder_magellen_test) {
+MU_TEST(subghz_encoder_magellan_test) {
mu_assert(
- subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
- "Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
}
MU_TEST(subghz_encoder_intertechno_v3_test) {
@@ -563,6 +565,12 @@ MU_TEST(subghz_encoder_clemsa_test) {
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
+MU_TEST(subghz_encoder_ansonic_test) {
+ mu_assert(
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
+}
+
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
@@ -600,10 +608,10 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_doitrand_test);
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
- MU_RUN_TEST(subghz_decoder_magellen_test);
+ MU_RUN_TEST(subghz_decoder_magellan_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test);
- MU_RUN_TEST(subghz_decoder_oregon2_test);
+ MU_RUN_TEST(subghz_decoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test);
@@ -622,9 +630,10 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_doitrand_test);
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
- MU_RUN_TEST(subghz_encoder_magellen_test);
+ MU_RUN_TEST(subghz_encoder_magellan_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test);
+ MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_random_test);
subghz_test_deinit();
diff --git a/applications/examples/example_images/ReadMe.md b/applications/examples/example_images/ReadMe.md
new file mode 100644
index 000000000..d884a0a97
--- /dev/null
+++ b/applications/examples/example_images/ReadMe.md
@@ -0,0 +1,24 @@
+# Application icons
+To use icons, do the following:
+* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located
+* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest
+* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension
+
+## Example
+We have an application with the following manifest:
+```
+App(
+ appid="example_images",
+ ...
+ fap_icon_assets="images",
+)
+```
+
+So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file.
+
+The example code is located in `example_images_main.c` and contains the following line:
+```
+#include "example_images_icons.h"
+```
+
+Image `dolphin_71x25.png` is available as `I_dolphin_71x25`.
diff --git a/applications/main/application.fam b/applications/main/application.fam
index 94aff522e..ef4933630 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -4,20 +4,25 @@ App(
apptype=FlipperAppType.METAPACKAGE,
provides=[
"clock_loader",
- "gpio",
+ # "gpio",
+ "gpio_loader",
# "ibutton",
"ibutton_loader",
- "infrared",
+ # "infrared",
+ "infrared_loader",
"lfrfid",
+ # "lfrfid_loader",
"nfc",
"subghz",
- "bad_usb",
+ #"bad_usb",
+ "bad_usb_loader",
# "u2f",
"u2f_loader",
"fap_loader",
"archive",
# "Clock",
- "SubGHz_Remote",
+ #"SubGHz_Remote",
+ "SubGHz_Remote_loader",
# "Spectrum_Analyzer",
],
)
diff --git a/applications/main/archive/application.fam b/applications/main/archive/application.fam
index f0a980ab0..309cee8d5 100644
--- a/applications/main/archive/application.fam
+++ b/applications/main/archive/application.fam
@@ -5,7 +5,7 @@ App(
entry_point="archive_app",
cdefines=["APP_ARCHIVE"],
requires=["gui"],
- stack_size=4 * 1024,
+ stack_size=6 * 1024,
icon="A_FileManager_14",
order=0,
)
diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c
index 01ba4a994..4155c0afb 100644
--- a/applications/main/archive/helpers/archive_browser.c
+++ b/applications/main/archive/helpers/archive_browser.c
@@ -64,7 +64,13 @@ static void
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
} else {
with_view_model(
- browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true);
+ browser->view,
+ ArchiveBrowserViewModel * model,
+ {
+ files_array_sort(model->files);
+ model->list_loading = false;
+ },
+ true);
}
}
diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c
index 1bf51a2b0..5c06c1bda 100644
--- a/applications/main/archive/helpers/archive_files.c
+++ b/applications/main/archive/helpers/archive_files.c
@@ -32,6 +32,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
if(is_folder) {
file->type = ArchiveFileTypeFolder;
} else {
+ char tmp_extension[MAX_EXT_LEN];
+ path_extract_extension(file->path, tmp_extension, MAX_EXT_LEN);
+ if((strcmp(tmp_extension, ".txt") == 0) || (strcmp(tmp_extension, ".md") == 0)) {
+ file->is_text_file = true;
+ }
file->type = ArchiveFileTypeUnknown;
}
}
diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h
index d7b25a64b..db624f5b5 100644
--- a/applications/main/archive/helpers/archive_files.h
+++ b/applications/main/archive/helpers/archive_files.h
@@ -2,6 +2,8 @@
#include
#include
+#include
+#include
#include
#include "toolbox/path.h"
@@ -29,6 +31,7 @@ typedef struct {
FuriString* custom_name;
bool fav;
bool is_app;
+ bool is_text_file;
} ArchiveFile_t;
static void ArchiveFile_t_init(ArchiveFile_t* obj) {
@@ -38,6 +41,7 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) {
obj->custom_name = furi_string_alloc();
obj->fav = false;
obj->is_app = false;
+ obj->is_text_file = false;
}
static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
@@ -52,6 +56,7 @@ static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src)
obj->custom_name = furi_string_alloc_set(src->custom_name);
obj->fav = src->fav;
obj->is_app = src->is_app;
+ obj->is_text_file = src->is_text_file;
}
static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
@@ -66,6 +71,7 @@ static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
furi_string_set(obj->custom_name, src->custom_name);
obj->fav = src->fav;
obj->is_app = src->is_app;
+ obj->is_text_file = src->is_text_file;
}
static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
@@ -77,13 +83,26 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
furi_string_free(obj->custom_name);
}
-ARRAY_DEF(
- files_array,
- ArchiveFile_t,
- (INIT(API_2(ArchiveFile_t_init)),
- SET(API_6(ArchiveFile_t_set)),
- INIT_SET(API_6(ArchiveFile_t_init_set)),
- CLEAR(API_2(ArchiveFile_t_clear))))
+static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
+ if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
+ return -1;
+ }
+
+ return furi_string_cmp(a->path, b->path);
+}
+
+#define M_OPL_ArchiveFile_t() \
+ (INIT(API_2(ArchiveFile_t_init)), \
+ SET(API_6(ArchiveFile_t_set)), \
+ INIT_SET(API_6(ArchiveFile_t_init_set)), \
+ CLEAR(API_2(ArchiveFile_t_clear)), \
+ CMP(API_6(ArchiveFile_t_cmp)), \
+ SWAP(M_SWAP_DEFAULT), \
+ EQUAL(API_6(M_EQUAL_DEFAULT)))
+
+ARRAY_DEF(files_array, ArchiveFile_t)
+
+ALGO_DEF(files_array, ARRAY_OPLIST(files_array, M_OPL_ArchiveFile_t()))
void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app);
bool archive_get_items(void* context, const char* path);
diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c
index dbf221990..ea7548750 100644
--- a/applications/main/archive/scenes/archive_scene_browser.c
+++ b/applications/main/archive/scenes/archive_scene_browser.c
@@ -45,10 +45,55 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec
if(param != NULL) {
param++;
}
- status = loader_start(loader, flipper_app_name[selected->type], param);
+
+ if(strcmp(flipper_app_name[selected->type], "U2F") == 0) {
+ char* tmpType = "/ext/apps/Main/U2F.fapΒ―";
+ char* result =
+ malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1);
+
+ strcpy(result, tmpType);
+ strcat(result, furi_string_get_cstr(selected->path));
+ status = loader_start(loader, "Applications", result);
+ } else {
+ status = loader_start(loader, flipper_app_name[selected->type], param);
+ }
} else {
- status = loader_start(
- loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path));
+ if(strcmp(flipper_app_name[selected->type], "iButton") == 0) {
+ char* tmpType = "/ext/apps/Main/ibutton.fapΒ―";
+ char* result =
+ malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1);
+
+ strcpy(result, tmpType);
+ strcat(result, furi_string_get_cstr(selected->path));
+ status = loader_start(loader, "Applications", result);
+ } else if(strcmp(flipper_app_name[selected->type], "Bad USB") == 0) {
+ char* tmpType = "/ext/apps/Main/bad_usb.fapΒ―";
+ char* result =
+ malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1);
+
+ strcpy(result, tmpType);
+ strcat(result, furi_string_get_cstr(selected->path));
+ status = loader_start(loader, "Applications", result);
+ // } else if(strcmp(flipper_app_name[selected->type], "125 kHz RFID") == 0) {
+ // char* tmpType = "/ext/apps/Main/lfrfid.fapΒ―";
+ // char* result =
+ // malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1);
+
+ // strcpy(result, tmpType);
+ // strcat(result, furi_string_get_cstr(selected->path));
+ // status = loader_start(loader, "Applications", result);
+ } else if(strcmp(flipper_app_name[selected->type], "Infrared") == 0) {
+ char* tmpType = "/ext/apps/Main/infrared.fapΒ―";
+ char* result =
+ malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1);
+
+ strcpy(result, tmpType);
+ strcat(result, furi_string_get_cstr(selected->path));
+ status = loader_start(loader, "Applications", result);
+ } else {
+ status = loader_start(
+ loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path));
+ }
}
if(status != LoaderStatusOk) {
@@ -150,6 +195,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneInfo);
consumed = true;
break;
+ case ArchiveBrowserEventFileMenuShow:
+ archive_show_file_menu(browser, false);
+ scene_manager_set_scene_state(
+ archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT);
+ scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow);
+ consumed = true;
+ break;
case ArchiveBrowserEventFileMenuDelete:
if(archive_get_tab(browser) != ArchiveTabFavorites) {
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);
diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h
index 6dda7a20d..d16c6f1a6 100644
--- a/applications/main/archive/scenes/archive_scene_config.h
+++ b/applications/main/archive/scenes/archive_scene_config.h
@@ -2,3 +2,4 @@ ADD_SCENE(archive, browser, Browser)
ADD_SCENE(archive, rename, Rename)
ADD_SCENE(archive, delete, Delete)
ADD_SCENE(archive, info, Info)
+ADD_SCENE(archive, show, Show)
diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c
index 6c7a90cb4..c2f2ddad5 100644
--- a/applications/main/archive/scenes/archive_scene_delete.c
+++ b/applications/main/archive/scenes/archive_scene_delete.c
@@ -48,11 +48,16 @@ bool archive_scene_delete_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeRight) {
+ // Show loading popup on delete
+ view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewStack);
+ archive_show_loading_popup(app, true);
+
if(selected->is_app) {
archive_app_delete_file(browser, name);
} else {
archive_delete_file(browser, "%s", name);
}
+ archive_show_loading_popup(app, false);
archive_show_file_menu(browser, false);
return scene_manager_previous_scene(app->scene_manager);
} else if(event.event == GuiButtonTypeLeft) {
diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c
index 5512b0619..346383162 100644
--- a/applications/main/archive/scenes/archive_scene_rename.c
+++ b/applications/main/archive/scenes/archive_scene_rename.c
@@ -64,6 +64,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
ArchiveFile_t* file = archive_get_current_file(archive->browser);
FuriString* path_dst;
+
path_dst = furi_string_alloc();
if(file->type == ArchiveFileTypeFolder) {
diff --git a/applications/main/archive/scenes/archive_scene_show.c b/applications/main/archive/scenes/archive_scene_show.c
new file mode 100644
index 000000000..416c10ded
--- /dev/null
+++ b/applications/main/archive/scenes/archive_scene_show.c
@@ -0,0 +1,147 @@
+#include "../archive_i.h"
+#include "../helpers/archive_browser.h"
+#include
+
+#define TAG "Archive"
+
+#define SHOW_MAX_FILE_SIZE 5000
+
+void archive_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) {
+ furi_assert(context);
+ ArchiveApp* app = (ArchiveApp*)context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, result);
+ }
+}
+
+static bool text_show_read_lines(File* file, FuriString* str_result) {
+ //furi_string_reset(str_result);
+ uint8_t buffer[SHOW_MAX_FILE_SIZE];
+
+ uint16_t read_count = storage_file_read(file, buffer, SHOW_MAX_FILE_SIZE);
+ if(storage_file_get_error(file) != FSE_OK) {
+ return false;
+ }
+
+ for(uint16_t i = 0; i < read_count; i++) {
+ furi_string_push_back(str_result, buffer[i]);
+ }
+
+ return true;
+}
+
+void archive_scene_show_on_enter(void* context) {
+ furi_assert(context);
+ ArchiveApp* instance = context;
+
+ FuriString* filename;
+ filename = furi_string_alloc();
+
+ FuriString* buffer;
+ buffer = furi_string_alloc();
+
+ ArchiveFile_t* current = archive_get_current_file(instance->browser);
+ Storage* fs_api = furi_record_open(RECORD_STORAGE);
+ File* file = storage_file_alloc(fs_api);
+
+ FileInfo fileinfo;
+ FS_Error error = storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo);
+ if(error == FSE_OK) {
+ if((fileinfo.size < SHOW_MAX_FILE_SIZE) && (fileinfo.size > 2)) {
+ bool ok = storage_file_open(
+ file, furi_string_get_cstr(current->path), FSAM_READ, FSOM_OPEN_EXISTING);
+ if(ok) {
+ if(!text_show_read_lines(file, buffer)) {
+ goto text_file_read_err;
+ }
+ if(!furi_string_size(buffer)) {
+ goto text_file_read_err;
+ }
+
+ storage_file_seek(file, 0, true);
+
+ widget_add_text_scroll_element(
+ instance->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer));
+
+ } else {
+ text_file_read_err:
+ widget_add_text_box_element(
+ instance->widget,
+ 0,
+ 0,
+ 128,
+ 64,
+ AlignLeft,
+ AlignCenter,
+ "\e#Error:\nStorage file open error\e#",
+ false);
+ }
+ storage_file_close(file);
+ } else if(fileinfo.size < 2) {
+ widget_add_text_box_element(
+ instance->widget,
+ 0,
+ 0,
+ 128,
+ 64,
+ AlignLeft,
+ AlignCenter,
+ "\e#Error:\nFile is too small\e#",
+ false);
+ } else {
+ widget_add_text_box_element(
+ instance->widget,
+ 0,
+ 0,
+ 128,
+ 64,
+ AlignLeft,
+ AlignCenter,
+ "\e#Error:\nFile is too large to show\e#",
+ false);
+ }
+ } else {
+ widget_add_text_box_element(
+ instance->widget,
+ 0,
+ 0,
+ 128,
+ 64,
+ AlignLeft,
+ AlignCenter,
+ "\e#Error:\nFile system error\e#",
+ false);
+ }
+ path_extract_filename(current->path, filename, false);
+
+ // This one to return and cursor select this file
+ path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename);
+ strlcpy(instance->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN);
+
+ furi_string_free(buffer);
+
+ storage_file_free(file);
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_free(filename);
+
+ view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget);
+}
+
+bool archive_scene_show_on_event(void* context, SceneManagerEvent event) {
+ furi_assert(context);
+ ArchiveApp* app = (ArchiveApp*)context;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_next_scene(app->scene_manager, ArchiveAppSceneBrowser);
+ return true;
+ }
+ return false;
+}
+
+void archive_scene_show_on_exit(void* context) {
+ furi_assert(context);
+ ArchiveApp* app = (ArchiveApp*)context;
+
+ widget_reset(app->widget);
+}
diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c
index cf25e7c89..1dc87319b 100644
--- a/applications/main/archive/views/archive_browser_view.c
+++ b/applications/main/archive/views/archive_browser_view.c
@@ -51,6 +51,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
FuriString* item_run = furi_string_alloc_set("Run In App");
FuriString* item_pin = furi_string_alloc_set("Pin");
FuriString* item_info = furi_string_alloc_set("Info");
+ FuriString* item_show = furi_string_alloc_set("Show");
FuriString* item_rename = furi_string_alloc_set("Rename");
FuriString* item_delete = furi_string_alloc_set("Delete");
@@ -79,6 +80,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_info,
ArchiveBrowserEventFileMenuInfo);
+ if(selected->is_text_file) {
+ archive_menu_add_item(
+ menu_array_push_raw(model->context_menu),
+ item_show,
+ ArchiveBrowserEventFileMenuShow);
+ }
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_rename,
@@ -100,6 +107,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_pin,
ArchiveBrowserEventFileMenuPin);
+ if(selected->type <= ArchiveFileTypeBadUsb) {
+ archive_menu_add_item(
+ menu_array_push_raw(model->context_menu),
+ item_show,
+ ArchiveBrowserEventFileMenuShow);
+ }
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_rename,
@@ -114,6 +127,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_info,
ArchiveBrowserEventFileMenuInfo);
+ if(selected->type <= ArchiveFileTypeBadUsb) {
+ archive_menu_add_item(
+ menu_array_push_raw(model->context_menu),
+ item_show,
+ ArchiveBrowserEventFileMenuShow);
+ }
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_pin,
@@ -136,6 +155,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_info,
ArchiveBrowserEventFileMenuInfo);
+ if(selected->type <= ArchiveFileTypeBadUsb) {
+ archive_menu_add_item(
+ menu_array_push_raw(model->context_menu),
+ item_show,
+ ArchiveBrowserEventFileMenuShow);
+ }
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_rename,
@@ -149,6 +174,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
furi_string_free(item_run);
furi_string_free(item_pin);
furi_string_free(item_info);
+ furi_string_free(item_show);
furi_string_free(item_rename);
furi_string_free(item_delete);
} /*else {
@@ -160,9 +186,9 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
canvas_set_color(canvas, ColorWhite);
uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu) * line_height);
- canvas_draw_box(canvas, 71, 11, 57, calc_height + 4);
+ canvas_draw_box(canvas, 71, 1, 57, calc_height + 4);
canvas_set_color(canvas, ColorBlack);
- elements_slightly_rounded_frame(canvas, 70, 12, 58, calc_height + 4);
+ elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4);
/*FURI_LOG_D(
TAG,
@@ -172,10 +198,10 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
model->menu_idx);*/
for(size_t i = 0; i < size_menu; i++) {
ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i);
- canvas_draw_str(canvas, 82, 21 + i * line_height, furi_string_get_cstr(current->text));
+ canvas_draw_str(canvas, 82, 11 + i * line_height, furi_string_get_cstr(current->text));
}
- canvas_draw_icon(canvas, 74, 14 + model->menu_idx * line_height, &I_ButtonRight_4x7);
+ canvas_draw_icon(canvas, 74, 4 + model->menu_idx * line_height, &I_ButtonRight_4x7);
}
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) {
diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h
index 67b50cb02..7d01e5387 100644
--- a/applications/main/archive/views/archive_browser_view.h
+++ b/applications/main/archive/views/archive_browser_view.h
@@ -40,6 +40,7 @@ typedef enum {
ArchiveBrowserEventFileMenuRename,
ArchiveBrowserEventFileMenuDelete,
ArchiveBrowserEventFileMenuInfo,
+ ArchiveBrowserEventFileMenuShow,
ArchiveBrowserEventFileMenuClose,
ArchiveBrowserEventEnterDir,
diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam
index 4da34f0de..d068bc8a2 100644
--- a/applications/main/bad_usb/application.fam
+++ b/applications/main/bad_usb/application.fam
@@ -1,7 +1,7 @@
App(
appid="bad_usb",
name="Bad USB",
- apptype=FlipperAppType.APP,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="bad_usb_app",
cdefines=["APP_BAD_USB"],
requires=[
@@ -9,6 +9,10 @@ App(
"dialogs",
],
stack_size=2 * 1024,
- icon="A_BadUsb_14",
+ # icon="A_BadUsb_14",
order=70,
+ fap_category="Main",
+ fap_icon="badusb_10px.png",
+ fap_icon_assets="images",
+ fap_libs=["assets"],
)
diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h
index e950c0198..eda67eae5 100644
--- a/applications/main/bad_usb/bad_usb_app_i.h
+++ b/applications/main/bad_usb/bad_usb_app_i.h
@@ -5,6 +5,7 @@
#include "bad_usb_script.h"
#include
+#include
#include
#include
#include
diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c
index 93b4026f5..e03b0f17d 100644
--- a/applications/main/bad_usb/bad_usb_script.c
+++ b/applications/main/bad_usb/bad_usb_script.c
@@ -86,7 +86,7 @@ static const DuckyKey ducky_keys[] = {
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
- {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK},
+ {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
{"SPACE", HID_KEYBOARD_SPACEBAR},
{"TAB", HID_KEYBOARD_TAB},
{"MENU", HID_KEYBOARD_APPLICATION},
@@ -243,12 +243,8 @@ static int32_t
const char* line_tmp = furi_string_get_cstr(line);
bool state = false;
- for(uint32_t i = 0; i < line_len; i++) {
- if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) {
- line_tmp = &line_tmp[i];
- break; // Skip spaces and tabs
- }
- if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
+ if(line_len == 0) {
+ return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
}
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
@@ -347,10 +343,6 @@ static int32_t
furi_hal_hid_kb_release(key);
return (0);
}
- if(error != NULL) {
- strncpy(error, "Unknown error", error_len);
- }
- return SCRIPT_STATE_ERROR;
}
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
@@ -463,19 +455,20 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
bad_usb->st.line_cur++;
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
bad_usb->buf_start = i + 1;
+ furi_string_trim(bad_usb->line);
delay_val = ducky_parse_line(
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
-
- if(delay_val < 0) {
+ if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
+ return 0;
+ } else if(delay_val < 0) {
bad_usb->st.error_line = bad_usb->st.line_cur;
if(delay_val == SCRIPT_STATE_NEXT_LINE) {
snprintf(
bad_usb->st.error, sizeof(bad_usb->st.error), "Forbidden empty line");
FURI_LOG_E(
- WORKER_TAG, "Forbidden empty line at line %lu", bad_usb->st.line_cur);
+ WORKER_TAG, "Forbidden empty line at line %u", bad_usb->st.line_cur);
} else {
- FURI_LOG_E(
- WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur);
+ FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
}
return SCRIPT_STATE_ERROR;
} else {
@@ -541,12 +534,16 @@ static int32_t bad_usb_worker(void* context) {
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever);
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
+ FuriFlagWaitAny,
+ FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) {
worker_state = BadUsbStateIdle; // Ready to run
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadUsbStateWillRun; // Will run when USB is connected
}
bad_usb->st.state = worker_state;
@@ -573,6 +570,31 @@ static int32_t bad_usb_worker(void* context) {
}
bad_usb->st.state = worker_state;
+ } else if(worker_state == BadUsbStateWillRun) { // State: start on connection
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
+ FuriFlagWaitAny,
+ FuriWaitForever);
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtConnect) { // Start executing script
+ DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
+ delay_val = 0;
+ bad_usb->buf_len = 0;
+ bad_usb->st.line_cur = 0;
+ bad_usb->defdelay = 0;
+ bad_usb->repeat_cnt = 0;
+ bad_usb->file_end = false;
+ storage_file_seek(script_file, 0, true);
+ // extra time for PC to recognize Flipper as keyboard
+ furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
+ worker_state = BadUsbStateRunning;
+ } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
+ worker_state = BadUsbStateNotConnected;
+ }
+ bad_usb->st.state = worker_state;
+
} else if(worker_state == BadUsbStateRunning) { // State: running
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
uint32_t flags = furi_thread_flags_wait(
@@ -650,7 +672,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
furi_assert(file_path);
- BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
+ BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773
bad_usb->file_path = furi_string_alloc();
furi_string_set(bad_usb->file_path, file_path);
bad_usb_script_set_default_keyboard_layout(bad_usb);
@@ -658,12 +680,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
bad_usb->st.state = BadUsbStateInit;
bad_usb->st.error[0] = '\0';
- bad_usb->thread = furi_thread_alloc();
- furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
- furi_thread_set_stack_size(bad_usb->thread, 2048);
- furi_thread_set_context(bad_usb->thread, bad_usb);
- furi_thread_set_callback(bad_usb->thread, bad_usb_worker);
-
+ bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
furi_thread_start(bad_usb->thread);
return bad_usb;
}
diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h
index c8b3a1653..1e4d98fe7 100644
--- a/applications/main/bad_usb/bad_usb_script.h
+++ b/applications/main/bad_usb/bad_usb_script.h
@@ -12,6 +12,7 @@ typedef enum {
BadUsbStateInit,
BadUsbStateNotConnected,
BadUsbStateIdle,
+ BadUsbStateWillRun,
BadUsbStateRunning,
BadUsbStateDelay,
BadUsbStateDone,
diff --git a/applications/main/bad_usb/badusb_10px.png b/applications/main/bad_usb/badusb_10px.png
new file mode 100644
index 000000000..037474aa3
Binary files /dev/null and b/applications/main/bad_usb/badusb_10px.png differ
diff --git a/applications/main/bad_usb/images/ActiveConnection_50x64.png b/applications/main/bad_usb/images/ActiveConnection_50x64.png
new file mode 100644
index 000000000..1d7686ddd
Binary files /dev/null and b/applications/main/bad_usb/images/ActiveConnection_50x64.png differ
diff --git a/applications/main/bad_usb/images/Clock_18x18.png b/applications/main/bad_usb/images/Clock_18x18.png
new file mode 100644
index 000000000..ab06d008e
Binary files /dev/null and b/applications/main/bad_usb/images/Clock_18x18.png differ
diff --git a/applications/main/bad_usb/images/Error_18x18.png b/applications/main/bad_usb/images/Error_18x18.png
new file mode 100644
index 000000000..16a5a74d9
Binary files /dev/null and b/applications/main/bad_usb/images/Error_18x18.png differ
diff --git a/applications/main/bad_usb/images/EviSmile1_18x21.png b/applications/main/bad_usb/images/EviSmile1_18x21.png
new file mode 100644
index 000000000..987af3258
Binary files /dev/null and b/applications/main/bad_usb/images/EviSmile1_18x21.png differ
diff --git a/applications/main/bad_usb/images/EviSmile2_18x21.png b/applications/main/bad_usb/images/EviSmile2_18x21.png
new file mode 100644
index 000000000..7e28c9f01
Binary files /dev/null and b/applications/main/bad_usb/images/EviSmile2_18x21.png differ
diff --git a/applications/main/bad_usb/images/EviWaiting1_18x21.png b/applications/main/bad_usb/images/EviWaiting1_18x21.png
new file mode 100644
index 000000000..d39d21733
Binary files /dev/null and b/applications/main/bad_usb/images/EviWaiting1_18x21.png differ
diff --git a/applications/main/bad_usb/images/EviWaiting2_18x21.png b/applications/main/bad_usb/images/EviWaiting2_18x21.png
new file mode 100644
index 000000000..15ca088fd
Binary files /dev/null and b/applications/main/bad_usb/images/EviWaiting2_18x21.png differ
diff --git a/applications/main/bad_usb/images/Percent_10x14.png b/applications/main/bad_usb/images/Percent_10x14.png
new file mode 100644
index 000000000..677911fd4
Binary files /dev/null and b/applications/main/bad_usb/images/Percent_10x14.png differ
diff --git a/applications/main/bad_usb/images/SDQuestion_35x43.png b/applications/main/bad_usb/images/SDQuestion_35x43.png
new file mode 100644
index 000000000..9b9c9a58e
Binary files /dev/null and b/applications/main/bad_usb/images/SDQuestion_35x43.png differ
diff --git a/applications/main/bad_usb/images/Smile_18x18.png b/applications/main/bad_usb/images/Smile_18x18.png
new file mode 100644
index 000000000..d2aae0dc3
Binary files /dev/null and b/applications/main/bad_usb/images/Smile_18x18.png differ
diff --git a/applications/main/bad_usb/images/UsbTree_48x22.png b/applications/main/bad_usb/images/UsbTree_48x22.png
new file mode 100644
index 000000000..cc41b5b9a
Binary files /dev/null and b/applications/main/bad_usb/images/UsbTree_48x22.png differ
diff --git a/applications/main/bad_usb/images/badusb_10px.png b/applications/main/bad_usb/images/badusb_10px.png
new file mode 100644
index 000000000..037474aa3
Binary files /dev/null and b/applications/main/bad_usb/images/badusb_10px.png differ
diff --git a/applications/main/bad_usb/images/keyboard_10px.png b/applications/main/bad_usb/images/keyboard_10px.png
new file mode 100644
index 000000000..74a10e6db
Binary files /dev/null and b/applications/main/bad_usb/images/keyboard_10px.png differ
diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c
index a090afc62..d2c30b99e 100644
--- a/applications/main/bad_usb/views/bad_usb_view.c
+++ b/applications/main/bad_usb/views/bad_usb_view.c
@@ -2,6 +2,7 @@
#include "../bad_usb_script.h"
#include
#include
+#include
#define MAX_NAME_LEN 64
@@ -44,10 +45,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
- if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
+ if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
+ (model->state.state == BadUsbStateNotConnected)) {
elements_button_center(canvas, "Run");
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
elements_button_center(canvas, "Stop");
+ } else if(model->state.state == BadUsbStateWillRun) {
+ elements_button_center(canvas, "Cancel");
}
if((model->state.state == BadUsbStateNotConnected) ||
@@ -60,6 +64,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB");
+ } else if(model->state.state == BadUsbStateWillRun) {
+ canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
+ canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
} else if(model->state.state == BadUsbStateFileError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
diff --git a/applications/main/bad_usb_loader/application.fam b/applications/main/bad_usb_loader/application.fam
new file mode 100644
index 000000000..d3bc81cf0
--- /dev/null
+++ b/applications/main/bad_usb_loader/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="bad_usb_loader",
+ name="Bad USB",
+ apptype=FlipperAppType.APP,
+ entry_point="bad_usb_loader_app",
+ requires=[
+ "gui",
+ "dialogs",
+ ],
+ stack_size=int(2 * 1024),
+ icon="A_BadUsb_14",
+ order=70,
+ link="/ext/apps/Main/bad_usb.fap",
+)
diff --git a/applications/main/bad_usb_loader/bad_usb_loader_app.c b/applications/main/bad_usb_loader/bad_usb_loader_app.c
new file mode 100644
index 000000000..cede52b55
--- /dev/null
+++ b/applications/main/bad_usb_loader/bad_usb_loader_app.c
@@ -0,0 +1,9 @@
+#include
+
+#define TAG "bad_usb_loader_app"
+
+int32_t bad_usb_loader_app(void* p) {
+ UNUSED(p);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/plugins/clock_app/ClockIcon.png b/applications/main/clock/ClockIcon.png
similarity index 100%
rename from applications/plugins/clock_app/ClockIcon.png
rename to applications/main/clock/ClockIcon.png
diff --git a/applications/main/clock/application.fam b/applications/main/clock/application.fam
new file mode 100644
index 000000000..a5486947a
--- /dev/null
+++ b/applications/main/clock/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="Clock",
+ name="Clock",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="clock_app",
+ cdefines=["APP_CLOCK"],
+ requires=["gui"],
+ # icon="A_Clock_14",
+ stack_size=2 * 1024,
+ order=9,
+ fap_icon="ClockIcon.png",
+ fap_category="Main",
+ fap_icon_assets="icons",
+)
\ No newline at end of file
diff --git a/applications/plugins/clock_app/clock_app.c b/applications/main/clock/clock_app.c
similarity index 87%
rename from applications/plugins/clock_app/clock_app.c
rename to applications/main/clock/clock_app.c
index f923f7c96..ee445db71 100644
--- a/applications/plugins/clock_app/clock_app.c
+++ b/applications/main/clock/clock_app.c
@@ -7,6 +7,7 @@
#include
#include
#include "applications/settings/desktop_settings/desktop_settings_app.h"
+#include "Clock_icons.h"
#define TAG "Clock"
@@ -200,6 +201,43 @@ const NotificationSequence clock_alert_startStop = {
NULL,
};
+const NotificationMessage message_red_127 = {
+ .type = NotificationMessageTypeLedRed,
+ .data.led.value = 0x7F,
+};
+
+const NotificationMessage message_green_127 = {
+ .type = NotificationMessageTypeLedGreen,
+ .data.led.value = 0x7F,
+};
+
+const NotificationMessage message_blue_127 = {
+ .type = NotificationMessageTypeLedBlue,
+ .data.led.value = 0x7F,
+};
+
+const NotificationSequence sequence_rainbow = {
+ &message_red_255, &message_green_0, &message_blue_0,
+ &message_delay_250, &message_red_255, &message_green_127,
+ &message_blue_0, &message_delay_250, &message_red_255,
+ &message_green_255, &message_blue_0, &message_delay_250,
+ &message_red_127, &message_green_255, &message_blue_0,
+ &message_delay_250, &message_red_0, &message_green_255,
+ &message_blue_0, &message_delay_250, &message_red_0,
+ &message_green_255, &message_blue_127, &message_delay_250,
+ &message_red_0, &message_green_255, &message_blue_255,
+ &message_delay_250, &message_red_0, &message_green_127,
+ &message_blue_255, &message_delay_250, &message_red_0,
+ &message_green_0, &message_blue_255, &message_delay_250,
+ &message_red_127, &message_green_0, &message_blue_255,
+ &message_delay_250, &message_red_255, &message_green_0,
+ &message_blue_255, &message_delay_250, &message_red_255,
+ &message_green_0, &message_blue_127, &message_delay_250,
+ &message_red_127, &message_green_127, &message_blue_127,
+ &message_delay_250, &message_red_255, &message_green_255,
+ &message_blue_255, &message_delay_250, NULL,
+};
+
static void desktop_view_main_dumbmode_changed(DesktopSettings* settings) {
settings->is_dumbmode = !settings->is_dumbmode;
DESKTOP_SETTINGS_SAVE(settings);
@@ -260,8 +298,20 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) {
int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) :
timer_stopped_seconds;
snprintf(strings[2], 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60);
+ int32_t elapsed_secs_img = (elapsed_secs % 60) % 5;
+ int32_t elapsed_secs_img2 = (elapsed_secs % 60) % 4;
+ // int32_t elapsed_secs_img3 = (elapsed_secs % 60) % 4;
+ static const Icon* const count_anim[5] = {
+ &I_HappyFlipper_128x64, &I_G0ku, &I_g0ku_1, &I_g0ku_2, &I_g0ku_3};
+ static const Icon* const count_anim2[4] = {
+ &I_EviWaiting1_18x21, &I_EviWaiting2_18x21, &I_EviSmile1_18x21, &I_EviSmile2_18x21};
+ static const Icon* const count_anim3[4] = {
+ &I_frame_01, &I_frame_02, &I_frame_03, &I_frame_02};
+ canvas_draw_icon(canvas, -5, 15, count_anim[elapsed_secs_img]);
+ canvas_draw_icon(canvas, 90, 0, count_anim2[elapsed_secs_img2]);
+ canvas_draw_icon(canvas, 110, 5, count_anim3[elapsed_secs_img2]);
canvas_draw_str_aligned(
- canvas, 64, 40, AlignCenter, AlignTop, strings[2]); // DRAW TIMER
+ canvas, 64, 32, AlignCenter, AlignTop, strings[2]); // DRAW TIMER
}
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, strings[1]); // DRAW TIME
canvas_set_font(canvas, FontBatteryPercent);
@@ -294,7 +344,9 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) {
elements_button_right(canvas, "S:ByMin");
}
}
- if(state->w_test) canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8);
+ if(state->w_test && state->desktop_settings->is_dumbmode) {
+ canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8);
+ }
}
static void clock_state_init(ClockState* const state) {
@@ -393,6 +445,7 @@ int32_t clock_app(void* p) {
plugin_state->codeSequence++;
if(plugin_state->codeSequence == 8) {
desktop_view_main_dumbmode_changed(plugin_state->desktop_settings);
+ plugin_state->w_test = true; // OH HEY NOW LETS GAIN EXP & MORE FUN
}
} else {
plugin_state->codeSequence = 0;
@@ -472,6 +525,8 @@ int32_t clock_app(void* p) {
if(plugin_state->codeSequence < (uint32_t)-1) processing = false;
}
break;
+ default:
+ break;
}
if(plugin_state->codeSequence == 10) {
plugin_state->codeSequence = 0;
@@ -481,9 +536,10 @@ int32_t clock_app(void* p) {
if(plugin_state->songSelect == 1 || plugin_state->songSelect == 2 ||
plugin_state->songSelect == 3) {
notification_message(notification, &sequence_success);
+ notification_message(notification, &sequence_rainbow);
+ notification_message(notification, &sequence_rainbow);
}
plugin_state->militaryTime = true; // 24 HR TIME FOR THIS
- plugin_state->w_test = true; // OH HEY NOW LETS GAIN EXP & MORE FUN
DOLPHIN_DEED(getRandomDeed());
}
} else if(event.input.type == InputTypeLong) {
@@ -525,6 +581,8 @@ int32_t clock_app(void* p) {
}
if(plugin_state->timerSecs == plugin_state->alert_time + 2) {
notification_message(notification, &clock_alert_pr3);
+ notification_message(notification, &sequence_rainbow);
+ notification_message(notification, &sequence_rainbow);
}
} else if(plugin_state->songSelect == 2) {
if(plugin_state->timerSecs == plugin_state->alert_time) {
@@ -543,6 +601,8 @@ int32_t clock_app(void* p) {
}
if(plugin_state->timerSecs == plugin_state->alert_time + 2) {
notification_message(notification, &clock_alert_mario3);
+ notification_message(notification, &sequence_rainbow);
+ notification_message(notification, &sequence_rainbow);
}
} else {
if(plugin_state->timerSecs == plugin_state->alert_time) {
diff --git a/applications/main/clock/icons/EviSmile1_18x21.png b/applications/main/clock/icons/EviSmile1_18x21.png
new file mode 100644
index 000000000..987af3258
Binary files /dev/null and b/applications/main/clock/icons/EviSmile1_18x21.png differ
diff --git a/applications/main/clock/icons/EviSmile2_18x21.png b/applications/main/clock/icons/EviSmile2_18x21.png
new file mode 100644
index 000000000..7e28c9f01
Binary files /dev/null and b/applications/main/clock/icons/EviSmile2_18x21.png differ
diff --git a/applications/main/clock/icons/EviWaiting1_18x21.png b/applications/main/clock/icons/EviWaiting1_18x21.png
new file mode 100644
index 000000000..d39d21733
Binary files /dev/null and b/applications/main/clock/icons/EviWaiting1_18x21.png differ
diff --git a/applications/main/clock/icons/EviWaiting2_18x21.png b/applications/main/clock/icons/EviWaiting2_18x21.png
new file mode 100644
index 000000000..15ca088fd
Binary files /dev/null and b/applications/main/clock/icons/EviWaiting2_18x21.png differ
diff --git a/applications/main/clock/icons/G0ku.png b/applications/main/clock/icons/G0ku.png
new file mode 100644
index 000000000..84389c4f4
Binary files /dev/null and b/applications/main/clock/icons/G0ku.png differ
diff --git a/applications/main/clock/icons/GameMode_11x8.png b/applications/main/clock/icons/GameMode_11x8.png
new file mode 100644
index 000000000..49f2e25bf
Binary files /dev/null and b/applications/main/clock/icons/GameMode_11x8.png differ
diff --git a/applications/main/clock/icons/HappyFlipper_128x64.png b/applications/main/clock/icons/HappyFlipper_128x64.png
new file mode 100644
index 000000000..d95412f3f
Binary files /dev/null and b/applications/main/clock/icons/HappyFlipper_128x64.png differ
diff --git a/applications/main/clock/icons/frame_01.png b/applications/main/clock/icons/frame_01.png
new file mode 100644
index 000000000..0cd187053
Binary files /dev/null and b/applications/main/clock/icons/frame_01.png differ
diff --git a/applications/main/clock/icons/frame_02.png b/applications/main/clock/icons/frame_02.png
new file mode 100644
index 000000000..3c37cdb8b
Binary files /dev/null and b/applications/main/clock/icons/frame_02.png differ
diff --git a/applications/main/clock/icons/frame_03.png b/applications/main/clock/icons/frame_03.png
new file mode 100644
index 000000000..a111ce163
Binary files /dev/null and b/applications/main/clock/icons/frame_03.png differ
diff --git a/applications/main/clock/icons/g0ku_1.png b/applications/main/clock/icons/g0ku_1.png
new file mode 100644
index 000000000..900d5113f
Binary files /dev/null and b/applications/main/clock/icons/g0ku_1.png differ
diff --git a/applications/main/clock/icons/g0ku_2.png b/applications/main/clock/icons/g0ku_2.png
new file mode 100644
index 000000000..202353bb9
Binary files /dev/null and b/applications/main/clock/icons/g0ku_2.png differ
diff --git a/applications/main/clock/icons/g0ku_3.png b/applications/main/clock/icons/g0ku_3.png
new file mode 100644
index 000000000..517c1653f
Binary files /dev/null and b/applications/main/clock/icons/g0ku_3.png differ
diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam
index bd0403e0e..f83f58e68 100644
--- a/applications/main/fap_loader/application.fam
+++ b/applications/main/fap_loader/application.fam
@@ -3,11 +3,12 @@ App(
name="Applications",
apptype=FlipperAppType.APP,
entry_point="fap_loader_app",
+ cdefines=["APP_FAP_LOADER"],
requires=[
"gui",
"storage",
],
stack_size=int(1.5 * 1024),
icon="A_Plugins_14",
- order=90,
+ order=9,
)
diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c
index 6c909aeeb..71d10341f 100644
--- a/applications/main/fap_loader/fap_loader_app.c
+++ b/applications/main/fap_loader/fap_loader_app.c
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
#include
@@ -16,6 +17,7 @@ struct FapLoader {
DialogsApp* dialogs;
Gui* gui;
FuriString* fap_path;
+ FuriString* fap_args;
ViewDispatcher* view_dispatcher;
Loading* loading;
};
@@ -101,16 +103,28 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
}
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
- FURI_LOG_I(TAG, "FAP Loader is staring app");
+ FURI_LOG_I(TAG, "FAP Loader is starting app");
- FuriThread* thread = flipper_application_spawn(loader->app, NULL);
- furi_thread_start(thread);
- furi_thread_join(thread);
+ if(strcmp(furi_string_get_cstr(loader->fap_args), "false") == 0) {
+ FuriThread* thread = flipper_application_spawn(loader->app, NULL);
+ furi_thread_start(thread);
+ furi_thread_join(thread);
- show_error = false;
- int ret = furi_thread_get_return_code(thread);
+ show_error = false;
+ int ret = furi_thread_get_return_code(thread);
- FURI_LOG_I(TAG, "FAP app returned: %i", ret);
+ FURI_LOG_I(TAG, "FAP app returned: %i", ret);
+ } else {
+ FuriThread* thread = flipper_application_spawn(
+ loader->app, (void*)furi_string_get_cstr(loader->fap_args));
+ furi_thread_start(thread);
+ furi_thread_join(thread);
+
+ show_error = false;
+ int ret = furi_thread_get_return_code(thread);
+
+ FURI_LOG_I(TAG, "FAP app returned: %i", ret);
+ }
} while(0);
if(show_error) {
@@ -154,8 +168,28 @@ static bool fap_loader_select_app(FapLoader* loader) {
}
static FapLoader* fap_loader_alloc(const char* path) {
- FapLoader* loader = malloc(sizeof(FapLoader));
- loader->fap_path = furi_string_alloc_set(path);
+ FapLoader* loader = malloc(sizeof(FapLoader)); //-V773
+
+ char* tmp = malloc(strlen(path) + 1);
+ strcpy(tmp, path);
+ char* new_path;
+
+ new_path = strtok(tmp, "Β―");
+
+ if(new_path) {
+ loader->fap_path = furi_string_alloc_set(new_path);
+ } else {
+ loader->fap_path = furi_string_alloc_set(path);
+ }
+
+ new_path = strtok(NULL, "Β―");
+
+ if(new_path) {
+ loader->fap_args = furi_string_alloc_set(new_path);
+ } else {
+ loader->fap_args = furi_string_alloc_set("false");
+ }
+
loader->storage = furi_record_open(RECORD_STORAGE);
loader->dialogs = furi_record_open(RECORD_DIALOGS);
loader->gui = furi_record_open(RECORD_GUI);
@@ -172,6 +206,7 @@ static void fap_loader_free(FapLoader* loader) {
loading_free(loader->loading);
view_dispatcher_free(loader->view_dispatcher);
furi_string_free(loader->fap_path);
+ furi_string_free(loader->fap_args);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_STORAGE);
diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam
index 1d7deb6f1..8c6fa3897 100644
--- a/applications/main/gpio/application.fam
+++ b/applications/main/gpio/application.fam
@@ -1,13 +1,15 @@
App(
appid="gpio",
name="GPIO",
- apptype=FlipperAppType.APP,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="gpio_app",
cdefines=["APP_GPIO"],
requires=["gui"],
stack_size=1 * 1024,
- icon="A_GPIO_14",
+ # icon="A_GPIO_14",
order=50,
- # fap_icon="gpioIcon.png",
- # fap_category="Main",
+ fap_icon="gpioIcon.png",
+ fap_category="Main",
+ fap_icon_assets="images",
+ fap_libs=["assets"],
)
diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c
index b8afdc8ea..19e53f453 100644
--- a/applications/main/gpio/gpio_app.c
+++ b/applications/main/gpio/gpio_app.c
@@ -51,6 +51,16 @@ GpioApp* gpio_app_alloc() {
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
+ app->gpio_i2c_scanner = gpio_i2c_scanner_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ GpioAppViewI2CScanner,
+ gpio_i2c_scanner_get_view(app->gpio_i2c_scanner));
+
+ app->gpio_i2c_sfp = gpio_i2c_sfp_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, GpioAppViewI2CSfp, gpio_i2c_sfp_get_view(app->gpio_i2c_sfp));
+
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget));
@@ -75,6 +85,8 @@ void gpio_app_free(GpioApp* app) {
// Views
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
+ view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CScanner);
+ view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CSfp);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
@@ -82,6 +94,8 @@ void gpio_app_free(GpioApp* app) {
widget_free(app->widget);
gpio_test_free(app->gpio_test);
gpio_usb_uart_free(app->gpio_usb_uart);
+ gpio_i2c_scanner_free(app->gpio_i2c_scanner);
+ gpio_i2c_sfp_free(app->gpio_i2c_sfp);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h
index fa91e8f79..05a3fcccd 100644
--- a/applications/main/gpio/gpio_app_i.h
+++ b/applications/main/gpio/gpio_app_i.h
@@ -15,6 +15,9 @@
#include
#include "views/gpio_test.h"
#include "views/gpio_usb_uart.h"
+#include "views/gpio_i2c_scanner.h"
+#include "views/gpio_i2c_sfp.h"
+#include
struct GpioApp {
Gui* gui;
@@ -24,9 +27,12 @@ struct GpioApp {
Widget* widget;
VariableItemList* var_item_list;
+ VariableItem* var_item_flow;
GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart;
UsbUartBridge* usb_uart_bridge;
+ GpioI2CScanner* gpio_i2c_scanner;
+ GpioI2CSfp* gpio_i2c_sfp;
};
typedef enum {
@@ -35,4 +41,6 @@ typedef enum {
GpioAppViewUsbUart,
GpioAppViewUsbUartCfg,
GpioAppViewUsbUartCloseRpc,
+ GpioAppViewI2CScanner,
+ GpioAppViewI2CSfp
} GpioAppView;
diff --git a/applications/main/gpio/gpio_custom_event.h b/applications/main/gpio/gpio_custom_event.h
index 2bf3e5a8b..02e24f753 100644
--- a/applications/main/gpio/gpio_custom_event.h
+++ b/applications/main/gpio/gpio_custom_event.h
@@ -5,6 +5,8 @@ typedef enum {
GpioStartEventOtgOn,
GpioStartEventManualControl,
GpioStartEventUsbUart,
+ GpioStartEventI2CScanner,
+ GpioStartEventI2CSfp,
GpioCustomEventErrorBack,
diff --git a/applications/main/gpio/gpio_i2c_scanner_control.c b/applications/main/gpio/gpio_i2c_scanner_control.c
new file mode 100644
index 000000000..bf3f1090d
--- /dev/null
+++ b/applications/main/gpio/gpio_i2c_scanner_control.c
@@ -0,0 +1,23 @@
+#include "gpio_i2c_scanner_control.h"
+#include
+
+void gpio_i2c_scanner_run_once(I2CScannerState* i2c_scanner_state) {
+ //Reset the number of items for rewriting the array
+ i2c_scanner_state->items = 0;
+ furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
+
+ uint32_t response_timeout_ticks = furi_ms_to_ticks(5.f);
+
+ //Addresses 0 to 7 are reserved and won't be scanned
+ for(int i = FIRST_NON_RESERVED_I2C_ADDRESS; i <= HIGHEST_I2C_ADDRESS; i++) {
+ if(furi_hal_i2c_is_device_ready(
+ &furi_hal_i2c_handle_external,
+ i << 1,
+ response_timeout_ticks)) { //Bitshift of 1 bit to convert 7-Bit Address into 8-Bit Address
+ i2c_scanner_state->responding_address[i2c_scanner_state->items] = i;
+ i2c_scanner_state->items++;
+ }
+ }
+
+ furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+}
diff --git a/applications/main/gpio/gpio_i2c_scanner_control.h b/applications/main/gpio/gpio_i2c_scanner_control.h
new file mode 100644
index 000000000..011f8aa36
--- /dev/null
+++ b/applications/main/gpio/gpio_i2c_scanner_control.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+#define FIRST_NON_RESERVED_I2C_ADDRESS 8
+#define HIGHEST_I2C_ADDRESS 127
+#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120
+
+typedef struct {
+ uint8_t items;
+ uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES];
+} I2CScannerState;
+
+/** Scans the I2C-Bus (SDA: Pin 15, SCL: Pin 16) for available 7-Bit slave addresses. Saves the number of detected slaves and their addresses.
+ *
+ * @param i2c_scanner_state State including the detected addresses and the number of addresses saved.
+ */
+void gpio_i2c_scanner_run_once(I2CScannerState* st);
diff --git a/applications/main/gpio/gpio_i2c_sfp_control.c b/applications/main/gpio/gpio_i2c_sfp_control.c
new file mode 100644
index 000000000..d14ba1b8e
--- /dev/null
+++ b/applications/main/gpio/gpio_i2c_sfp_control.c
@@ -0,0 +1,334 @@
+#include "gpio_i2c_sfp_control.h"
+#include
+#include
+
+// This is map mapping the connector type to the appropriate name. (see SFF-8024 Rev. 4.9, Table 4-3)
+const char* sfp_connector_map[256] = {
+ "Unknown or unspecified",
+ "SC",
+ "Fibre Channel Style 1",
+ "Fibre Channel Style 2",
+ "BNC/TNC",
+ "Fibre Channel Coax",
+ "Fiber Jack",
+ "LC",
+ "MT-RJ",
+ "MU",
+ "SG",
+ "Optical Pigtail",
+ "MP0 1x12",
+ "MP 2x16",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "HSSDC II",
+ "Copper pigtail",
+ "RJ45",
+ "No seperable connector",
+ "MXC 2x16",
+ "CS optical connector",
+ "SN (prev. Mini CS)",
+ "MPO 2x12",
+ "MPO 1x16",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved"};
+
+void str_part(uint8_t* data, char* buffer, int start, int len) {
+ int i;
+ for(i = start; i < start + len; i++) {
+ buffer[i - start] = data[i];
+ }
+ buffer[i - start] = '\0';
+}
+
+/*
+void print_part(uint8_t *data, char *ptype, char *prefix, int start, int len){
+ printf("%s: ", prefix);
+ for (int i=start; ivendor, 20, 16);
+ str_part(sfp_data, i2c_sfp_state->rev, 56, 4);
+ str_part(sfp_data, i2c_sfp_state->pn, 40, 16);
+ str_part(sfp_data, i2c_sfp_state->sn, 68, 16);
+ str_part(sfp_data, i2c_sfp_state->dc, 84, 6);
+
+ //Look up connector in table and copy to struct.
+ strcpy(i2c_sfp_state->connector, sfp_connector_map[sfp_data[2]]);
+ i2c_sfp_state->bitrate = sfp_data[12] * 100;
+ i2c_sfp_state->wavelength = sfp_data[60] * 256 + sfp_data[61];
+ i2c_sfp_state->sm_reach = sfp_data[14];
+ i2c_sfp_state->mm_reach_om3 = sfp_data[19] * 10;
+ }
+ furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+}
diff --git a/applications/main/gpio/gpio_i2c_sfp_control.h b/applications/main/gpio/gpio_i2c_sfp_control.h
new file mode 100644
index 000000000..7a9f4bcbe
--- /dev/null
+++ b/applications/main/gpio/gpio_i2c_sfp_control.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+#define FIRST_NON_RESERVED_I2C_ADDRESS 8
+#define HIGHEST_I2C_ADDRESS 127
+#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120
+#define SFP_I2C_ADDRESS 0x50
+
+typedef struct {
+ char vendor[32];
+ char oui[32];
+ char rev[32];
+ char pn[32];
+ char sn[32];
+ char dc[32];
+ uint8_t type;
+ char connector[32];
+ int wavelength;
+ int sm_reach;
+ int mm_reach_om3;
+ int bitrate;
+} I2CSfpState;
+
+/** Reads data from a connected SFP on I2C-Bus (SDA: Pin 15, SCL: Pin 16). Saves data from SFP.
+ *
+ * @param i2c_sfp_state Data collected from SFP.
+ */
+void gpio_i2c_sfp_run_once(I2CSfpState* st);
diff --git a/applications/main/gpio/images/ActiveConnection_50x64.png b/applications/main/gpio/images/ActiveConnection_50x64.png
new file mode 100644
index 000000000..1d7686ddd
Binary files /dev/null and b/applications/main/gpio/images/ActiveConnection_50x64.png differ
diff --git a/applications/main/gpio/images/ArrowDownEmpty_14x15.png b/applications/main/gpio/images/ArrowDownEmpty_14x15.png
new file mode 100644
index 000000000..8c6d54f9c
Binary files /dev/null and b/applications/main/gpio/images/ArrowDownEmpty_14x15.png differ
diff --git a/applications/main/gpio/images/ArrowDownFilled_14x15.png b/applications/main/gpio/images/ArrowDownFilled_14x15.png
new file mode 100644
index 000000000..6cef0f4a7
Binary files /dev/null and b/applications/main/gpio/images/ArrowDownFilled_14x15.png differ
diff --git a/applications/main/gpio/images/ArrowUpEmpty_14x15.png b/applications/main/gpio/images/ArrowUpEmpty_14x15.png
new file mode 100644
index 000000000..261c6d89e
Binary files /dev/null and b/applications/main/gpio/images/ArrowUpEmpty_14x15.png differ
diff --git a/applications/main/gpio/images/ArrowUpFilled_14x15.png b/applications/main/gpio/images/ArrowUpFilled_14x15.png
new file mode 100644
index 000000000..fa35eb2f8
Binary files /dev/null and b/applications/main/gpio/images/ArrowUpFilled_14x15.png differ
diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h
index 3406e42d3..faca22d8d 100644
--- a/applications/main/gpio/scenes/gpio_scene_config.h
+++ b/applications/main/gpio/scenes/gpio_scene_config.h
@@ -3,3 +3,5 @@ ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, usb_uart, UsbUart)
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)
+ADD_SCENE(gpio, i2c_scanner, I2CScanner)
+ADD_SCENE(gpio, i2c_sfp, I2CSfp)
diff --git a/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c b/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c
new file mode 100644
index 000000000..7b310debc
--- /dev/null
+++ b/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c
@@ -0,0 +1,36 @@
+#include "../gpio_app_i.h"
+#include
+
+static I2CScannerState* i2c_scanner_state;
+
+void gpio_scene_i2c_scanner_ok_callback(InputType type, void* context) {
+ furi_assert(context);
+ GpioApp* app = context;
+
+ if(type == InputTypeRelease) {
+ notification_message(app->notifications, &sequence_set_green_255);
+ gpio_i2c_scanner_run_once(i2c_scanner_state);
+ notification_message(app->notifications, &sequence_reset_green);
+ gpio_i2c_scanner_update_state(app->gpio_i2c_scanner, i2c_scanner_state);
+ }
+}
+
+void gpio_scene_i2c_scanner_on_enter(void* context) {
+ GpioApp* app = context;
+ i2c_scanner_state = malloc(sizeof(I2CScannerState));
+
+ gpio_i2c_scanner_set_ok_callback(
+ app->gpio_i2c_scanner, gpio_scene_i2c_scanner_ok_callback, app);
+ view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CScanner);
+}
+
+bool gpio_scene_i2c_scanner_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void gpio_scene_i2c_scanner_on_exit(void* context) {
+ UNUSED(context);
+ free(i2c_scanner_state);
+}
diff --git a/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c b/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c
new file mode 100644
index 000000000..49d0e8dd2
--- /dev/null
+++ b/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c
@@ -0,0 +1,35 @@
+#include "../gpio_app_i.h"
+#include
+
+static I2CSfpState* i2c_sfp_state;
+
+void gpio_scene_i2c_sfp_ok_callback(InputType type, void* context) {
+ furi_assert(context);
+ GpioApp* app = context;
+
+ if(type == InputTypeRelease) {
+ notification_message(app->notifications, &sequence_set_green_255);
+ gpio_i2c_sfp_run_once(i2c_sfp_state);
+ notification_message(app->notifications, &sequence_reset_green);
+ gpio_i2c_sfp_update_state(app->gpio_i2c_sfp, i2c_sfp_state);
+ }
+}
+
+void gpio_scene_i2c_sfp_on_enter(void* context) {
+ GpioApp* app = context;
+ i2c_sfp_state = malloc(sizeof(I2CSfpState));
+
+ gpio_i2c_sfp_set_ok_callback(app->gpio_i2c_sfp, gpio_scene_i2c_sfp_ok_callback, app);
+ view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CSfp);
+}
+
+bool gpio_scene_i2c_sfp_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void gpio_scene_i2c_sfp_on_exit(void* context) {
+ UNUSED(context);
+ free(i2c_sfp_state);
+}
diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c
index 729922949..08b77238f 100644
--- a/applications/main/gpio/scenes/gpio_scene_start.c
+++ b/applications/main/gpio/scenes/gpio_scene_start.c
@@ -1,12 +1,13 @@
#include "../gpio_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
-#include
enum GpioItem {
GpioItemUsbUart,
GpioItemTest,
GpioItemOtg,
+ GpioItemI2CScanner,
+ GpioItemI2CSfp,
};
enum GpioOtg {
@@ -27,6 +28,10 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl);
} else if(index == GpioItemUsbUart) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart);
+ } else if(index == GpioItemI2CScanner) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CScanner);
+ } else if(index == GpioItemI2CSfp) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CSfp);
}
}
@@ -68,6 +73,9 @@ void gpio_scene_start_on_enter(void* context) {
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
}
+ variable_item_list_add(var_item_list, "I2C-Scanner", 0, NULL, NULL);
+ variable_item_list_add(var_item_list, "I2C-SFP", 0, NULL, NULL);
+
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));
@@ -86,10 +94,15 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == GpioStartEventManualControl) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
+ } else if(event.event == GpioStartEventI2CScanner) {
+ scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CScanner);
+ scene_manager_next_scene(app->scene_manager, GpioSceneI2CScanner);
+ } else if(event.event == GpioStartEventI2CSfp) {
+ scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CSfp);
+ scene_manager_next_scene(app->scene_manager, GpioSceneI2CSfp);
} else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) {
- DOLPHIN_DEED(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);
diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
index 653a306aa..c114d79a2 100644
--- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
+++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
@@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"};
-static const char* flow_pins[] = {"None", "2,3", "6,7"};
+static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
static const char* baudrate_mode[] = {"Host"};
static const uint32_t baudrate_list[] = {
2400,
@@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
return false;
}
+void line_ensure_flow_invariant(GpioApp* app) {
+ // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is
+ // selected. This function enforces that invariant by resetting flow_pins
+ // to None if it is configured to 16,15 when LPUART is selected.
+
+ uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
+ VariableItem* item = app->var_item_flow;
+ variable_item_set_values_count(item, available_flow_pins);
+
+ if(cfg_set->flow_pins >= available_flow_pins) {
+ cfg_set->flow_pins = 0;
+ usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+
+ variable_item_set_current_value_index(item, cfg_set->flow_pins);
+ variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
+ }
+}
+
static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
@@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) {
else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
+ line_ensure_flow_invariant(app);
}
static void line_flow_cb(VariableItem* item) {
@@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
variable_item_set_current_value_index(item, cfg_set->uart_ch);
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]);
- item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app);
+ item = variable_item_list_add(
+ var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
+ app->var_item_flow = item;
+ line_ensure_flow_invariant(app);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));
diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c
index 6e0bce736..927eedb07 100644
--- a/applications/main/gpio/usb_uart_bridge.c
+++ b/applications/main/gpio/usb_uart_bridge.c
@@ -14,6 +14,7 @@
static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
{&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7
+ {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15
};
typedef enum {
@@ -158,11 +159,8 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- usb_uart->tx_thread = furi_thread_alloc();
- furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker");
- furi_thread_set_stack_size(usb_uart->tx_thread, 512);
- furi_thread_set_context(usb_uart->tx_thread, usb_uart);
- furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
+ usb_uart->tx_thread =
+ furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart);
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
@@ -183,7 +181,7 @@ static int32_t usb_uart_worker(void* context) {
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
- furi_check((events & FuriFlagError) == 0);
+ furi_check(!(events & FuriFlagError));
if(events & WorkerEvtStop) break;
if(events & WorkerEvtRxDone) {
size_t len = furi_stream_buffer_receive(
@@ -287,7 +285,7 @@ static int32_t usb_uart_tx_thread(void* context) {
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
- furi_check((events & FuriFlagError) == 0);
+ furi_check(!(events & FuriFlagError));
if(events & WorkerEvtTxStop) break;
if(events & WorkerEvtCdcRx) {
furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);
@@ -337,11 +335,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
- usb_uart->thread = furi_thread_alloc();
- furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
- furi_thread_set_stack_size(usb_uart->thread, 1024);
- furi_thread_set_context(usb_uart->thread, usb_uart);
- furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
+ usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
furi_thread_start(usb_uart->thread);
return usb_uart;
diff --git a/applications/main/gpio/views/gpio_i2c_scanner.c b/applications/main/gpio/views/gpio_i2c_scanner.c
new file mode 100644
index 000000000..0f00639d6
--- /dev/null
+++ b/applications/main/gpio/views/gpio_i2c_scanner.c
@@ -0,0 +1,139 @@
+#include
+#include "gpio_i2c_scanner.h"
+#include "../gpio_item.h"
+
+#include
+
+struct GpioI2CScanner {
+ View* view;
+ GpioI2CScannerOkCallback callback;
+ void* context;
+};
+
+typedef struct {
+ uint8_t items;
+ uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES];
+} GpioI2CScannerModel;
+
+static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event);
+
+static void gpio_i2c_scanner_draw_callback(Canvas* canvas, void* _model) {
+ GpioI2CScannerModel* model = _model;
+
+ char temp_str[25];
+ elements_button_center(canvas, "Start scan");
+ canvas_draw_line(canvas, 2, 10, 125, 10);
+ canvas_draw_line(canvas, 2, 52, 125, 52);
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str(canvas, 2, 9, "I2C-Scanner");
+ canvas_draw_str(canvas, 3, 25, "SDA:");
+ canvas_draw_str(canvas, 3, 42, "SCL:");
+
+ canvas_set_font(canvas, FontSecondary);
+ snprintf(temp_str, 25, "Slaves: %u", model->items);
+ canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str);
+
+ canvas_draw_str(canvas, 29, 25, "Pin 15");
+ canvas_draw_str(canvas, 29, 42, "Pin 16");
+
+ canvas_set_font(canvas, FontSecondary);
+
+ char temp_str2[6];
+ if(model->items > 0) {
+ snprintf(temp_str, 25, "Addr: ");
+ for(int i = 0; i < model->items; i++) {
+ snprintf(temp_str2, 6, "0x%x ", model->responding_address[i]);
+ strcat(temp_str, temp_str2);
+
+ if(i == 1 || model->items == 1) { //Draw a maximum of two addresses in the first line
+ canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, temp_str);
+ temp_str[0] = '\0';
+ } else if(
+ i == 4 || (model->items - 1 == i &&
+ i < 6)) { //Draw a maximum of three addresses in the second line
+ canvas_draw_str_aligned(canvas, 127, 36, AlignRight, AlignBottom, temp_str);
+ temp_str[0] = '\0';
+ } else if(i == 7 || model->items - 1 == i) { //Draw a maximum of three addresses in the third line
+ canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, temp_str);
+ break;
+ }
+ }
+ }
+}
+
+static bool gpio_i2c_scanner_input_callback(InputEvent* event, void* context) {
+ furi_assert(context);
+ GpioI2CScanner* gpio_i2c_scanner = context;
+ bool consumed = false;
+
+ if(event->key == InputKeyOk) {
+ consumed = gpio_i2c_scanner_process_ok(gpio_i2c_scanner, event);
+ }
+
+ return consumed;
+}
+
+static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event) {
+ bool consumed = false;
+ gpio_i2c_scanner->callback(event->type, gpio_i2c_scanner->context);
+
+ return consumed;
+}
+
+GpioI2CScanner* gpio_i2c_scanner_alloc() {
+ GpioI2CScanner* gpio_i2c_scanner = malloc(sizeof(GpioI2CScanner));
+
+ gpio_i2c_scanner->view = view_alloc();
+ view_allocate_model(gpio_i2c_scanner->view, ViewModelTypeLocking, sizeof(GpioI2CScannerModel));
+ view_set_context(gpio_i2c_scanner->view, gpio_i2c_scanner);
+ view_set_draw_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_draw_callback);
+ view_set_input_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_input_callback);
+
+ return gpio_i2c_scanner;
+}
+
+void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner) {
+ furi_assert(gpio_i2c_scanner);
+ view_free(gpio_i2c_scanner->view);
+ free(gpio_i2c_scanner);
+}
+
+View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner) {
+ furi_assert(gpio_i2c_scanner);
+ return gpio_i2c_scanner->view;
+}
+
+void gpio_i2c_scanner_set_ok_callback(
+ GpioI2CScanner* gpio_i2c_scanner,
+ GpioI2CScannerOkCallback callback,
+ void* context) {
+ furi_assert(gpio_i2c_scanner);
+ furi_assert(callback);
+ with_view_model(
+ gpio_i2c_scanner->view,
+ GpioI2CScannerModel * model,
+ {
+ UNUSED(model);
+ gpio_i2c_scanner->callback = callback;
+ gpio_i2c_scanner->context = context;
+ },
+ false);
+}
+
+void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st) {
+ furi_assert(instance);
+ furi_assert(st);
+
+ with_view_model(
+ instance->view,
+ GpioI2CScannerModel * model,
+ {
+ model->items = st->items;
+
+ for(int i = 0; i < model->items; i++) {
+ model->responding_address[i] = st->responding_address[i];
+ }
+ },
+ true);
+}
diff --git a/applications/main/gpio/views/gpio_i2c_scanner.h b/applications/main/gpio/views/gpio_i2c_scanner.h
new file mode 100644
index 000000000..52ebf3de3
--- /dev/null
+++ b/applications/main/gpio/views/gpio_i2c_scanner.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+#include "../gpio_i2c_scanner_control.h"
+
+typedef struct GpioI2CScanner GpioI2CScanner;
+typedef void (*GpioI2CScannerOkCallback)(InputType type, void* context);
+
+GpioI2CScanner* gpio_i2c_scanner_alloc();
+
+void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner);
+
+View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner);
+
+void gpio_i2c_scanner_set_ok_callback(
+ GpioI2CScanner* gpio_i2c_scanner,
+ GpioI2CScannerOkCallback callback,
+ void* context);
+
+void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st);
diff --git a/applications/main/gpio/views/gpio_i2c_sfp.c b/applications/main/gpio/views/gpio_i2c_sfp.c
new file mode 100644
index 000000000..85492e783
--- /dev/null
+++ b/applications/main/gpio/views/gpio_i2c_sfp.c
@@ -0,0 +1,149 @@
+#include
+#include "gpio_i2c_sfp.h"
+#include "../gpio_item.h"
+
+#include
+
+struct GpioI2CSfp {
+ View* view;
+ GpioI2CSfpOkCallback callback;
+ void* context;
+};
+
+typedef struct {
+ char vendor[32];
+ char oui[32];
+ char rev[32];
+ char pn[32];
+ char sn[32];
+ char dc[32];
+ uint8_t type;
+ char connector[32];
+ int wavelength;
+ int sm_reach;
+ int mm_reach_om3;
+ int bitrate;
+} GpioI2CSfpModel;
+
+static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event);
+
+static void gpio_i2c_sfp_draw_callback(Canvas* canvas, void* _model) {
+ GpioI2CSfpModel* model = _model;
+
+ // Temp String for formatting output
+ char temp_str[280];
+
+ canvas_set_font(canvas, FontSecondary);
+ elements_button_center(canvas, "Read");
+ canvas_draw_str(canvas, 2, 63, "P15 SCL");
+ canvas_draw_str(canvas, 92, 63, "P16 SDA");
+
+ snprintf(temp_str, 280, "Vendor: %s", model->vendor);
+ canvas_draw_str(canvas, 2, 9, temp_str);
+
+ snprintf(temp_str, 280, "PN: %s", model->pn);
+ canvas_draw_str(canvas, 2, 19, temp_str);
+
+ snprintf(temp_str, 280, "SN: %s", model->sn);
+ canvas_draw_str(canvas, 2, 29, temp_str);
+
+ snprintf(temp_str, 280, "REV: %s", model->rev);
+ canvas_draw_str(canvas, 2, 39, temp_str);
+
+ snprintf(temp_str, 280, "CON: %s", model->connector);
+ canvas_draw_str(canvas, 50, 39, temp_str);
+
+ //Print Wavelength of Module
+ snprintf(temp_str, 280, "%u nm", model->wavelength);
+ canvas_draw_str(canvas, 2, 49, temp_str);
+
+ // These values will be zero if not applicable..
+ if(model->sm_reach != 0) {
+ snprintf(temp_str, 280, "%u km (SM)", model->sm_reach);
+ canvas_draw_str(canvas, 50, 49, temp_str);
+ }
+ if(model->mm_reach_om3 != 0) {
+ snprintf(temp_str, 280, "%u m (MM OM3)", model->mm_reach_om3);
+ canvas_draw_str(canvas, 50, 49, temp_str);
+ }
+}
+
+static bool gpio_i2c_sfp_input_callback(InputEvent* event, void* context) {
+ furi_assert(context);
+ GpioI2CSfp* gpio_i2c_sfp = context;
+ bool consumed = false;
+
+ if(event->key == InputKeyOk) {
+ consumed = gpio_i2c_sfp_process_ok(gpio_i2c_sfp, event);
+ }
+
+ return consumed;
+}
+
+static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event) {
+ bool consumed = false;
+ gpio_i2c_sfp->callback(event->type, gpio_i2c_sfp->context);
+
+ return consumed;
+}
+
+GpioI2CSfp* gpio_i2c_sfp_alloc() {
+ GpioI2CSfp* gpio_i2c_sfp = malloc(sizeof(GpioI2CSfp));
+
+ gpio_i2c_sfp->view = view_alloc();
+ view_allocate_model(gpio_i2c_sfp->view, ViewModelTypeLocking, sizeof(GpioI2CSfpModel));
+ view_set_context(gpio_i2c_sfp->view, gpio_i2c_sfp);
+ view_set_draw_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_draw_callback);
+ view_set_input_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_input_callback);
+
+ return gpio_i2c_sfp;
+}
+
+void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp) {
+ furi_assert(gpio_i2c_sfp);
+ view_free(gpio_i2c_sfp->view);
+ free(gpio_i2c_sfp);
+}
+
+View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp) {
+ furi_assert(gpio_i2c_sfp);
+ return gpio_i2c_sfp->view;
+}
+
+void gpio_i2c_sfp_set_ok_callback(
+ GpioI2CSfp* gpio_i2c_sfp,
+ GpioI2CSfpOkCallback callback,
+ void* context) {
+ furi_assert(gpio_i2c_sfp);
+ furi_assert(callback);
+ with_view_model(
+ gpio_i2c_sfp->view,
+ GpioI2CSfpModel * model,
+ {
+ UNUSED(model);
+ gpio_i2c_sfp->callback = callback;
+ gpio_i2c_sfp->context = context;
+ },
+ false);
+}
+
+void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st) {
+ furi_assert(instance);
+ furi_assert(st);
+
+ with_view_model(
+ instance->view,
+ GpioI2CSfpModel * model,
+ {
+ // Insert values into model...
+ strcpy(model->vendor, st->vendor);
+ strcpy(model->pn, st->pn);
+ strcpy(model->sn, st->sn);
+ strcpy(model->rev, st->rev);
+ strcpy(model->connector, st->connector);
+ model->wavelength = st->wavelength;
+ model->sm_reach = st->sm_reach;
+ model->mm_reach_om3 = st->mm_reach_om3;
+ },
+ true);
+}
diff --git a/applications/main/gpio/views/gpio_i2c_sfp.h b/applications/main/gpio/views/gpio_i2c_sfp.h
new file mode 100644
index 000000000..c57c143a4
--- /dev/null
+++ b/applications/main/gpio/views/gpio_i2c_sfp.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+#include "../gpio_i2c_sfp_control.h"
+
+typedef struct GpioI2CSfp GpioI2CSfp;
+typedef void (*GpioI2CSfpOkCallback)(InputType type, void* context);
+
+GpioI2CSfp* gpio_i2c_sfp_alloc();
+
+void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp);
+
+View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp);
+
+void gpio_i2c_sfp_set_ok_callback(
+ GpioI2CSfp* gpio_i2c_sfp,
+ GpioI2CSfpOkCallback callback,
+ void* context);
+
+void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st);
diff --git a/applications/main/gpio_loader/application.fam b/applications/main/gpio_loader/application.fam
new file mode 100644
index 000000000..08a5b7967
--- /dev/null
+++ b/applications/main/gpio_loader/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="gpio_loader",
+ name="GPIO",
+ apptype=FlipperAppType.APP,
+ entry_point="gpio_loader_app",
+ requires=[
+ "gui",
+ "storage",
+ ],
+ stack_size=int(1.5 * 1024),
+ icon="A_GPIO_14",
+ order=50,
+ link="/ext/apps/Main/gpio.fap",
+)
diff --git a/applications/main/gpio_loader/gpio_loader_app.c b/applications/main/gpio_loader/gpio_loader_app.c
new file mode 100644
index 000000000..ba7fb4e31
--- /dev/null
+++ b/applications/main/gpio_loader/gpio_loader_app.c
@@ -0,0 +1,9 @@
+#include
+
+#define TAG "gpio_loader_app"
+
+int32_t gpio_loader_app(void* p) {
+ UNUSED(p);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam
index 1265fb788..fc3333ae7 100644
--- a/applications/main/ibutton/application.fam
+++ b/applications/main/ibutton/application.fam
@@ -13,4 +13,6 @@ App(
order=60,
fap_icon="iBIcon.png",
fap_category="Main",
+ fap_icon_assets="images",
+ fap_libs=["assets"],
)
diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c
index 887fb3e75..2762d68e1 100644
--- a/applications/main/ibutton/ibutton.c
+++ b/applications/main/ibutton/ibutton.c
@@ -1,10 +1,10 @@
#include "ibutton.h"
-#include "assets_icons.h"
#include "ibutton_i.h"
#include "ibutton/scenes/ibutton_scene.h"
#include
#include
#include
+#include
#define TAG "iButtonApp"
@@ -337,11 +337,13 @@ int32_t ibutton_app(void* p) {
view_dispatcher_attach_to_gui(
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc);
+ DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
} else {
view_dispatcher_attach_to_gui(
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
if(key_loaded) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
} else {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);
}
diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h
index a9515195e..f79ae0d81 100644
--- a/applications/main/ibutton/ibutton_i.h
+++ b/applications/main/ibutton/ibutton_i.h
@@ -4,6 +4,7 @@
#include
#include
+#include "ibutton_icons.h"
#include
#include
#include
diff --git a/applications/main/ibutton/images/DolphinMafia_115x62.png b/applications/main/ibutton/images/DolphinMafia_115x62.png
new file mode 100644
index 000000000..53dffb4fa
Binary files /dev/null and b/applications/main/ibutton/images/DolphinMafia_115x62.png differ
diff --git a/applications/main/ibutton/images/DolphinNice_96x59.png b/applications/main/ibutton/images/DolphinNice_96x59.png
new file mode 100644
index 000000000..b111196c7
Binary files /dev/null and b/applications/main/ibutton/images/DolphinNice_96x59.png differ
diff --git a/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png b/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png
new file mode 100644
index 000000000..46f559f65
Binary files /dev/null and b/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png differ
diff --git a/applications/main/ibutton/images/DolphinWait_61x59.png b/applications/main/ibutton/images/DolphinWait_61x59.png
new file mode 100644
index 000000000..bb0c38400
Binary files /dev/null and b/applications/main/ibutton/images/DolphinWait_61x59.png differ
diff --git a/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png b/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png
new file mode 100644
index 000000000..2b4bec7c6
Binary files /dev/null and b/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png differ
diff --git a/applications/main/ibutton/images/iButtonKey_49x44.png b/applications/main/ibutton/images/iButtonKey_49x44.png
new file mode 100644
index 000000000..db895ec52
Binary files /dev/null and b/applications/main/ibutton/images/iButtonKey_49x44.png differ
diff --git a/applications/main/ibutton/images/ibutt_10px.png b/applications/main/ibutton/images/ibutt_10px.png
new file mode 100644
index 000000000..2fdaf123a
Binary files /dev/null and b/applications/main/ibutton/images/ibutt_10px.png differ
diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
index b3ec11a50..ccac76121 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
@@ -1,7 +1,5 @@
#include "../ibutton_i.h"
-#include
-
void ibutton_scene_add_type_byte_input_callback(void* context) {
iButton* ibutton = context;
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);
@@ -38,7 +36,6 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
consumed = true;
if(event.event == iButtonCustomEventByteEditResult) {
ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE);
- DOLPHIN_DEED(DolphinDeedIbuttonAdd);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
}
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
index 8b7d9dfc0..9ff165e4a 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
@@ -39,10 +39,5 @@ void ibutton_scene_delete_success_on_exit(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
-
- popup_disable_timeout(popup);
- popup_set_context(popup, NULL);
- popup_set_callback(popup, NULL);
+ popup_reset(popup);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
index b3bc38ead..6f6ffcf57 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
@@ -1,6 +1,5 @@
#include "../ibutton_i.h"
#include
-#include
#include
#define EMULATE_TIMEOUT_TICKS 10
@@ -26,8 +25,6 @@ void ibutton_scene_emulate_on_enter(void* context) {
path_extract_filename(ibutton->file_path, key_name, true);
}
- DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
-
// check that stored key has name
if(!furi_string_empty(key_name)) {
ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c
index 05920a0ad..1fe75e45a 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read.c
@@ -11,7 +11,6 @@ void ibutton_scene_read_on_enter(void* context) {
Popup* popup = ibutton->popup;
iButtonKey* key = ibutton->key;
iButtonWorker* worker = ibutton->key_worker;
- DOLPHIN_DEED(DolphinDeedIbuttonRead);
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
@@ -54,8 +53,8 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
if(success) {
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
- DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
+ DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
}
}
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
index 921b24fc1..0a8ecfa55 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
@@ -1,4 +1,5 @@
#include "../ibutton_i.h"
+#include
typedef enum {
SubmenuIndexSave,
@@ -49,6 +50,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
} else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
index 773b93e0c..5f25a0002 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
@@ -1,6 +1,7 @@
#include "../ibutton_i.h"
#include
#include
+#include
static void ibutton_scene_save_name_text_input_callback(void* context) {
iButton* ibutton = context;
@@ -57,6 +58,15 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(event.event == iButtonCustomEventTextEditResult) {
if(ibutton_save_key(ibutton, ibutton->text_store)) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);
+ if(scene_manager_has_previous_scene(
+ ibutton->scene_manager, iButtonSceneSavedKeyMenu)) {
+ // Nothing, do not count editing as saving
+ } else if(scene_manager_has_previous_scene(
+ ibutton->scene_manager, iButtonSceneAddType)) {
+ DOLPHIN_DEED(DolphinDeedIbuttonAdd);
+ } else {
+ DOLPHIN_DEED(DolphinDeedIbuttonSave);
+ }
} else {
const uint32_t possible_scenes[] = {
iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c
index 43237f429..8b16d2929 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c
@@ -1,5 +1,4 @@
#include "../ibutton_i.h"
-#include
static void ibutton_scene_save_success_popup_callback(void* context) {
iButton* ibutton = context;
@@ -9,7 +8,6 @@ static void ibutton_scene_save_success_popup_callback(void* context) {
void ibutton_scene_save_success_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- DOLPHIN_DEED(DolphinDeedIbuttonSave);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
@@ -41,10 +39,5 @@ void ibutton_scene_save_success_on_exit(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
-
- popup_disable_timeout(popup);
- popup_set_context(popup, NULL);
- popup_set_callback(popup, NULL);
+ popup_reset(popup);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
index 3d588dd02..e4c9c350a 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
@@ -1,4 +1,5 @@
#include "../ibutton_i.h"
+#include
enum SubmenuIndex {
SubmenuIndexEmulate,
@@ -58,6 +59,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even
consumed = true;
if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
} else if(event.event == SubmenuIndexEdit) {
diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c
index dde224e15..b8f6b07d6 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_start.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_start.c
@@ -1,5 +1,6 @@
#include "../ibutton_i.h"
#include "ibutton/scenes/ibutton_scene.h"
+#include
enum SubmenuIndex {
SubmenuIndexRead,
@@ -38,6 +39,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {
consumed = true;
if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);
+ DOLPHIN_DEED(DolphinDeedIbuttonRead);
} else if(event.event == SubmenuIndexSaved) {
furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c
index 3acb1dea0..17cd53d08 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c
@@ -43,10 +43,5 @@ void ibutton_scene_write_success_on_exit(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
-
- popup_disable_timeout(popup);
- popup_set_context(popup, NULL);
- popup_set_callback(popup, NULL);
+ popup_reset(popup);
}
diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam
index 897afdeb8..405224d80 100644
--- a/applications/main/infrared/application.fam
+++ b/applications/main/infrared/application.fam
@@ -1,25 +1,19 @@
App(
appid="infrared",
name="Infrared",
- apptype=FlipperAppType.APP,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="infrared_app",
cdefines=["APP_INFRARED"],
requires=[
"gui",
"dialogs",
],
- provides=["infrared_start"],
- icon="A_Infrared_14",
+ # provides=["infrared_start"],
+ # icon="A_Infrared_14",
stack_size=3 * 1024,
order=40,
- # fap_icon="irIcon.png",
- # fap_category="Main",
-)
-
-App(
- appid="infrared_start",
- apptype=FlipperAppType.STARTUP,
- entry_point="infrared_on_system_start",
- requires=["infrared"],
- order=20,
+ fap_category="Main",
+ fap_icon="ir_10px.png",
+ fap_icon_assets="images",
+ fap_libs=["assets"],
)
diff --git a/applications/main/infrared/images/DolphinMafia_115x62.png b/applications/main/infrared/images/DolphinMafia_115x62.png
new file mode 100644
index 000000000..53dffb4fa
Binary files /dev/null and b/applications/main/infrared/images/DolphinMafia_115x62.png differ
diff --git a/applications/main/infrared/images/DolphinNice_96x59.png b/applications/main/infrared/images/DolphinNice_96x59.png
new file mode 100644
index 000000000..b111196c7
Binary files /dev/null and b/applications/main/infrared/images/DolphinNice_96x59.png differ
diff --git a/applications/main/infrared/images/DolphinReadingSuccess_59x63.png b/applications/main/infrared/images/DolphinReadingSuccess_59x63.png
new file mode 100644
index 000000000..46f559f65
Binary files /dev/null and b/applications/main/infrared/images/DolphinReadingSuccess_59x63.png differ
diff --git a/applications/main/infrared/images/Down_25x27.png b/applications/main/infrared/images/Down_25x27.png
new file mode 100644
index 000000000..c13097778
Binary files /dev/null and b/applications/main/infrared/images/Down_25x27.png differ
diff --git a/applications/main/infrared/images/Down_hvr_25x27.png b/applications/main/infrared/images/Down_hvr_25x27.png
new file mode 100644
index 000000000..76d181924
Binary files /dev/null and b/applications/main/infrared/images/Down_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/InfraredArrowDown_4x8.png b/applications/main/infrared/images/InfraredArrowDown_4x8.png
new file mode 100644
index 000000000..2ac7bcdbe
Binary files /dev/null and b/applications/main/infrared/images/InfraredArrowDown_4x8.png differ
diff --git a/applications/main/infrared/images/InfraredArrowUp_4x8.png b/applications/main/infrared/images/InfraredArrowUp_4x8.png
new file mode 100644
index 000000000..4c9a16b3f
Binary files /dev/null and b/applications/main/infrared/images/InfraredArrowUp_4x8.png differ
diff --git a/applications/main/infrared/images/InfraredLearnShort_128x31.png b/applications/main/infrared/images/InfraredLearnShort_128x31.png
new file mode 100644
index 000000000..783ad0877
Binary files /dev/null and b/applications/main/infrared/images/InfraredLearnShort_128x31.png differ
diff --git a/applications/main/infrared/images/Mode_25x27.png b/applications/main/infrared/images/Mode_25x27.png
new file mode 100644
index 000000000..381ba8296
Binary files /dev/null and b/applications/main/infrared/images/Mode_25x27.png differ
diff --git a/applications/main/infrared/images/Mode_hvr_25x27.png b/applications/main/infrared/images/Mode_hvr_25x27.png
new file mode 100644
index 000000000..64f459f55
Binary files /dev/null and b/applications/main/infrared/images/Mode_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Mute_25x27.png b/applications/main/infrared/images/Mute_25x27.png
new file mode 100644
index 000000000..d8812dd4f
Binary files /dev/null and b/applications/main/infrared/images/Mute_25x27.png differ
diff --git a/applications/main/infrared/images/Mute_hvr_25x27.png b/applications/main/infrared/images/Mute_hvr_25x27.png
new file mode 100644
index 000000000..155bd9004
Binary files /dev/null and b/applications/main/infrared/images/Mute_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Pin_back_arrow_10x8.png b/applications/main/infrared/images/Pin_back_arrow_10x8.png
new file mode 100644
index 000000000..3bafabd14
Binary files /dev/null and b/applications/main/infrared/images/Pin_back_arrow_10x8.png differ
diff --git a/applications/main/infrared/images/Power_25x27.png b/applications/main/infrared/images/Power_25x27.png
new file mode 100644
index 000000000..5ae493fbe
Binary files /dev/null and b/applications/main/infrared/images/Power_25x27.png differ
diff --git a/applications/main/infrared/images/Power_hvr_25x27.png b/applications/main/infrared/images/Power_hvr_25x27.png
new file mode 100644
index 000000000..9425072c0
Binary files /dev/null and b/applications/main/infrared/images/Power_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/RFIDDolphinSend_97x61.png b/applications/main/infrared/images/RFIDDolphinSend_97x61.png
new file mode 100644
index 000000000..343b9f734
Binary files /dev/null and b/applications/main/infrared/images/RFIDDolphinSend_97x61.png differ
diff --git a/applications/main/infrared/images/Rotate_25x27.png b/applications/main/infrared/images/Rotate_25x27.png
new file mode 100644
index 000000000..648634a09
Binary files /dev/null and b/applications/main/infrared/images/Rotate_25x27.png differ
diff --git a/applications/main/infrared/images/Rotate_hvr_25x27.png b/applications/main/infrared/images/Rotate_hvr_25x27.png
new file mode 100644
index 000000000..a2b5cf93d
Binary files /dev/null and b/applications/main/infrared/images/Rotate_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/SDQuestion_35x43.png b/applications/main/infrared/images/SDQuestion_35x43.png
new file mode 100644
index 000000000..9b9c9a58e
Binary files /dev/null and b/applications/main/infrared/images/SDQuestion_35x43.png differ
diff --git a/applications/main/infrared/images/Swing_25x27.png b/applications/main/infrared/images/Swing_25x27.png
new file mode 100644
index 000000000..38a6c9040
Binary files /dev/null and b/applications/main/infrared/images/Swing_25x27.png differ
diff --git a/applications/main/infrared/images/Swing_hvr_25x27.png b/applications/main/infrared/images/Swing_hvr_25x27.png
new file mode 100644
index 000000000..6e65b4e2e
Binary files /dev/null and b/applications/main/infrared/images/Swing_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Timer_25x27.png b/applications/main/infrared/images/Timer_25x27.png
new file mode 100644
index 000000000..2f1853a34
Binary files /dev/null and b/applications/main/infrared/images/Timer_25x27.png differ
diff --git a/applications/main/infrared/images/Timer_hvr_25x27.png b/applications/main/infrared/images/Timer_hvr_25x27.png
new file mode 100644
index 000000000..d4dffa544
Binary files /dev/null and b/applications/main/infrared/images/Timer_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Up_25x27.png b/applications/main/infrared/images/Up_25x27.png
new file mode 100644
index 000000000..b81a02e8a
Binary files /dev/null and b/applications/main/infrared/images/Up_25x27.png differ
diff --git a/applications/main/infrared/images/Up_hvr_25x27.png b/applications/main/infrared/images/Up_hvr_25x27.png
new file mode 100644
index 000000000..cf71e5965
Binary files /dev/null and b/applications/main/infrared/images/Up_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Vol_down_25x27.png b/applications/main/infrared/images/Vol_down_25x27.png
new file mode 100644
index 000000000..d7ae44558
Binary files /dev/null and b/applications/main/infrared/images/Vol_down_25x27.png differ
diff --git a/applications/main/infrared/images/Vol_down_hvr_25x27.png b/applications/main/infrared/images/Vol_down_hvr_25x27.png
new file mode 100644
index 000000000..c556a037a
Binary files /dev/null and b/applications/main/infrared/images/Vol_down_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/Vol_up_25x27.png b/applications/main/infrared/images/Vol_up_25x27.png
new file mode 100644
index 000000000..c4d9e87a0
Binary files /dev/null and b/applications/main/infrared/images/Vol_up_25x27.png differ
diff --git a/applications/main/infrared/images/Vol_up_hvr_25x27.png b/applications/main/infrared/images/Vol_up_hvr_25x27.png
new file mode 100644
index 000000000..90c2df47d
Binary files /dev/null and b/applications/main/infrared/images/Vol_up_hvr_25x27.png differ
diff --git a/applications/main/infrared/images/ir_10px.png b/applications/main/infrared/images/ir_10px.png
new file mode 100644
index 000000000..22c986180
Binary files /dev/null and b/applications/main/infrared/images/ir_10px.png differ
diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c
deleted file mode 100644
index 5ec57c757..000000000
--- a/applications/main/infrared/infrared_cli.c
+++ /dev/null
@@ -1,366 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "infrared_signal.h"
-
-#define INFRARED_CLI_BUF_SIZE 10
-
-static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args);
-static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args);
-static void infrared_cli_process_decode(Cli* cli, FuriString* args);
-
-static const struct {
- const char* cmd;
- void (*process_function)(Cli* cli, FuriString* args);
-} infrared_cli_commands[] = {
- {.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
- {.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
- {.cmd = "decode", .process_function = infrared_cli_process_decode},
-};
-
-static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
- furi_assert(received_signal);
- char buf[100];
- size_t buf_cnt;
- Cli* cli = (Cli*)context;
-
- if(infrared_worker_signal_is_decoded(received_signal)) {
- const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
- buf_cnt = snprintf(
- buf,
- sizeof(buf),
- "%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
- infrared_get_protocol_name(message->protocol),
- ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
- message->address,
- ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4),
- message->command,
- message->repeat ? " R" : "");
- cli_write(cli, (uint8_t*)buf, buf_cnt);
- } else {
- const uint32_t* timings;
- size_t timings_cnt;
- infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
-
- buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt);
- cli_write(cli, (uint8_t*)buf, buf_cnt);
- for(size_t i = 0; i < timings_cnt; ++i) {
- buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]);
- cli_write(cli, (uint8_t*)buf, buf_cnt);
- }
- buf_cnt = snprintf(buf, sizeof(buf), "\r\n");
- cli_write(cli, (uint8_t*)buf, buf_cnt);
- }
-}
-
-static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
- UNUSED(cli);
- UNUSED(args);
- InfraredWorker* worker = infrared_worker_alloc();
- infrared_worker_rx_start(worker);
- infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
-
- printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n");
- while(!cli_cmd_interrupt_received(cli)) {
- furi_delay_ms(50);
- }
-
- infrared_worker_rx_stop(worker);
- infrared_worker_free(worker);
-}
-
-static void infrared_cli_print_usage(void) {
- printf("Usage:\r\n");
- printf("\tir rx\r\n");
- printf("\tir tx \r\n");
- printf("\t and are hex-formatted\r\n");
- printf("\tAvailable protocols:");
- for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) {
- printf(" %s", infrared_get_protocol_name((InfraredProtocol)i));
- }
- printf("\r\n");
- printf("\tRaw format:\r\n");
- printf("\tir tx RAW F: DC: ...\r\n");
- printf(
- "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
- INFRARED_MIN_FREQUENCY,
- INFRARED_MAX_FREQUENCY);
- printf("\tir decode []\r\n");
-}
-
-static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
- char protocol_name[32];
- InfraredMessage message;
- int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command);
-
- if(parsed != 3) {
- return false;
- }
-
- message.protocol = infrared_get_protocol_by_name(protocol_name);
- message.repeat = false;
- infrared_signal_set_message(signal, &message);
- return infrared_signal_is_valid(signal);
-}
-
-static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) {
- char frequency_str[INFRARED_CLI_BUF_SIZE];
- char duty_cycle_str[INFRARED_CLI_BUF_SIZE];
- int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
-
- if(parsed != 2) {
- return false;
- }
-
- uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT);
- uint32_t frequency = atoi(frequency_str);
- float duty_cycle = (float)atoi(duty_cycle_str) / 100;
-
- str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE;
-
- size_t timings_size = 0;
- while(1) {
- while(*str == ' ') {
- ++str;
- }
-
- char timing_str[INFRARED_CLI_BUF_SIZE];
- if(sscanf(str, "%9s", timing_str) != 1) {
- break;
- }
-
- str += strlen(timing_str);
- uint32_t timing = atoi(timing_str);
-
- if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) {
- break;
- }
-
- timings[timings_size] = timing;
- ++timings_size;
- }
-
- infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
- free(timings);
-
- return infrared_signal_is_valid(signal);
-}
-
-static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) {
- UNUSED(cli);
- const char* str = furi_string_get_cstr(args);
- InfraredSignal* signal = infrared_signal_alloc();
-
- bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal);
- if(success) {
- infrared_signal_transmit(signal);
- } else {
- printf("Wrong arguments.\r\n");
- infrared_cli_print_usage();
- }
-
- infrared_signal_free(signal);
-}
-
-static bool
- infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
- bool ret = infrared_signal_save(signal, file, name);
- if(!ret) {
- printf("Failed to save signal: \"%s\"\r\n", name);
- }
- return ret;
-}
-
-static bool infrared_cli_decode_raw_signal(
- InfraredRawSignal* raw_signal,
- InfraredDecoderHandler* decoder,
- FlipperFormat* output_file,
- const char* signal_name) {
- InfraredSignal* signal = infrared_signal_alloc();
- bool ret = false, level = true, is_decoded = false;
-
- size_t i;
- for(i = 0; i < raw_signal->timings_size; ++i) {
- // TODO: Any infrared_check_decoder_ready() magic?
- const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
-
- if(message) {
- is_decoded = true;
- printf(
- "Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
- infrared_get_protocol_name(message->protocol),
- message->address,
- message->command,
- (message->repeat ? "R" : ""));
- if(output_file && !message->repeat) {
- infrared_signal_set_message(signal, message);
- if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
- }
- }
-
- level = !level;
- }
-
- if(i == raw_signal->timings_size) {
- if(!is_decoded && output_file) {
- infrared_signal_set_raw_signal(
- signal,
- raw_signal->timings,
- raw_signal->timings_size,
- raw_signal->frequency,
- raw_signal->duty_cycle);
- ret = infrared_cli_save_signal(signal, output_file, signal_name);
- } else {
- ret = true;
- }
- }
-
- infrared_reset_decoder(decoder);
- infrared_signal_free(signal);
- return ret;
-}
-
-static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
- bool ret = false;
-
- InfraredSignal* signal = infrared_signal_alloc();
- InfraredDecoderHandler* decoder = infrared_alloc_decoder();
-
- FuriString* tmp;
- tmp = furi_string_alloc();
-
- while(infrared_signal_read(signal, input_file, tmp)) {
- ret = false;
- if(!infrared_signal_is_valid(signal)) {
- printf("Invalid signal\r\n");
- break;
- }
- if(!infrared_signal_is_raw(signal)) {
- if(output_file &&
- !infrared_cli_save_signal(signal, output_file, furi_string_get_cstr(tmp))) {
- break;
- } else {
- printf("Skipping decoded signal\r\n");
- continue;
- }
- }
- InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
- printf(
- "Raw signal: %s, %u samples\r\n", furi_string_get_cstr(tmp), raw_signal->timings_size);
- if(!infrared_cli_decode_raw_signal(
- raw_signal, decoder, output_file, furi_string_get_cstr(tmp)))
- break;
- ret = true;
- }
-
- infrared_free_decoder(decoder);
- infrared_signal_free(signal);
- furi_string_free(tmp);
-
- return ret;
-}
-
-static void infrared_cli_process_decode(Cli* cli, FuriString* args) {
- UNUSED(cli);
- Storage* storage = furi_record_open(RECORD_STORAGE);
- FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
- FlipperFormat* output_file = NULL;
-
- uint32_t version;
- FuriString *tmp, *header, *input_path, *output_path;
- tmp = furi_string_alloc();
- header = furi_string_alloc();
- input_path = furi_string_alloc();
- output_path = furi_string_alloc();
-
- do {
- if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
- printf("Wrong arguments.\r\n");
- infrared_cli_print_usage();
- break;
- }
- args_read_probably_quoted_string_and_trim(args, output_path);
- if(!flipper_format_buffered_file_open_existing(
- input_file, furi_string_get_cstr(input_path))) {
- printf(
- "Failed to open file for reading: \"%s\"\r\n", furi_string_get_cstr(input_path));
- break;
- }
- if(!flipper_format_read_header(input_file, header, &version) ||
- (!furi_string_start_with_str(header, "IR")) || version != 1) {
- printf(
- "Invalid or corrupted input file: \"%s\"\r\n", furi_string_get_cstr(input_path));
- break;
- }
- if(!furi_string_empty(output_path)) {
- printf("Writing output to file: \"%s\"\r\n", furi_string_get_cstr(output_path));
- output_file = flipper_format_file_alloc(storage);
- }
- if(output_file &&
- !flipper_format_file_open_always(output_file, furi_string_get_cstr(output_path))) {
- printf(
- "Failed to open file for writing: \"%s\"\r\n", furi_string_get_cstr(output_path));
- break;
- }
- if(output_file && !flipper_format_write_header(output_file, header, version)) {
- printf(
- "Failed to write to the output file: \"%s\"\r\n",
- furi_string_get_cstr(output_path));
- break;
- }
- if(!infrared_cli_decode_file(input_file, output_file)) {
- break;
- }
- printf("File successfully decoded.\r\n");
- } while(false);
-
- furi_string_free(tmp);
- furi_string_free(header);
- furi_string_free(input_path);
- furi_string_free(output_path);
-
- flipper_format_free(input_file);
- if(output_file) flipper_format_free(output_file);
- furi_record_close(RECORD_STORAGE);
-}
-
-static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) {
- UNUSED(context);
- if(furi_hal_infrared_is_busy()) {
- printf("INFRARED is busy. Exiting.");
- return;
- }
-
- FuriString* command;
- command = furi_string_alloc();
- args_read_string_and_trim(args, command);
-
- size_t i = 0;
- for(; i < COUNT_OF(infrared_cli_commands); ++i) {
- size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
- if(!strncmp(furi_string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
- break;
- }
- }
-
- if(i < COUNT_OF(infrared_cli_commands)) {
- infrared_cli_commands[i].process_function(cli, args);
- } else {
- infrared_cli_print_usage();
- }
-
- furi_string_free(command);
-}
-void infrared_on_system_start() {
-#ifdef SRV_CLI
- Cli* cli = (Cli*)furi_record_open(RECORD_CLI);
- cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
- furi_record_close(RECORD_CLI);
-#else
- UNUSED(infrared_cli_start_ir);
-#endif
-}
diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h
index 6d25d1609..4bf0631f5 100644
--- a/applications/main/infrared/infrared_i.h
+++ b/applications/main/infrared/infrared_i.h
@@ -32,6 +32,8 @@
#include "rpc/rpc_app.h"
+#include
+
#define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128
diff --git a/applications/main/infrared/irIcon.png b/applications/main/infrared/irIcon.png
deleted file mode 100644
index ccfcee9c8..000000000
Binary files a/applications/main/infrared/irIcon.png and /dev/null differ
diff --git a/applications/main/infrared/ir_10px.png b/applications/main/infrared/ir_10px.png
new file mode 100644
index 000000000..22c986180
Binary files /dev/null and b/applications/main/infrared/ir_10px.png differ
diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c
index 37f9b3e05..48699a71f 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn.c
@@ -1,4 +1,5 @@
#include "../infrared_i.h"
+#include
void infrared_scene_learn_on_enter(void* context) {
Infrared* infrared = context;
@@ -27,6 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
if(event.event == InfraredCustomEventTypeSignalReceived) {
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
+ DOLPHIN_DEED(DolphinDeedIrLearnSuccess);
consumed = true;
}
}
diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c
index 7d3571715..54b7da724 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn_done.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c
@@ -1,13 +1,10 @@
#include "../infrared_i.h"
-#include
-
void infrared_scene_learn_done_on_enter(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
- DOLPHIN_DEED(DolphinDeedIrSave);
if(infrared->app_state.is_learning_new_remote) {
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c
index b6a7eac0d..a8772a985 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c
@@ -1,4 +1,5 @@
#include "../infrared_i.h"
+#include
void infrared_scene_learn_enter_name_on_enter(void* context) {
Infrared* infrared = context;
@@ -49,6 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e
if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
+ DOLPHIN_DEED(DolphinDeedIrSave);
} else {
dialog_message_show_storage_error(infrared->dialogs, "Failed to save file");
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c
index 466627144..469d4de9e 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn_success.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c
@@ -1,7 +1,5 @@
#include "../infrared_i.h"
-#include
-
static void
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
Infrared* infrared = context;
@@ -13,7 +11,6 @@ void infrared_scene_learn_success_on_enter(void* context) {
DialogEx* dialog_ex = infrared->dialog_ex;
InfraredSignal* signal = infrared->received_signal;
- DOLPHIN_DEED(DolphinDeedIrLearnSuccess);
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
if(infrared_signal_is_raw(signal)) {
diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c
index 3c50f89e4..bb1f982c5 100644
--- a/applications/main/infrared/views/infrared_progress_view.c
+++ b/applications/main/infrared/views/infrared_progress_view.c
@@ -1,6 +1,5 @@
#include
#include "furi_hal_resources.h"
-#include "assets_icons.h"
#include "gui/canvas.h"
#include "gui/view.h"
#include "input/input.h"
@@ -9,6 +8,7 @@
#include "infrared_progress_view.h"
#include "gui/modules/button_panel.h"
#include
+#include
struct InfraredProgressView {
View* view;
diff --git a/applications/main/infrared_loader/application.fam b/applications/main/infrared_loader/application.fam
new file mode 100644
index 000000000..c3d87a24e
--- /dev/null
+++ b/applications/main/infrared_loader/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="infrared_loader",
+ name="Infrared",
+ apptype=FlipperAppType.APP,
+ entry_point="infrared_loader_app",
+ requires=[
+ "gui",
+ "dialogs",
+ ],
+ stack_size=int(2 * 1024),
+ icon="A_Infrared_14",
+ order=40,
+ link="/ext/apps/Main/infrared.fap",
+)
diff --git a/applications/main/infrared_loader/infrared_loader_app.c b/applications/main/infrared_loader/infrared_loader_app.c
new file mode 100644
index 000000000..1fd2a59ed
--- /dev/null
+++ b/applications/main/infrared_loader/infrared_loader_app.c
@@ -0,0 +1,9 @@
+#include
+
+#define TAG "infrared_loader_app"
+
+int32_t infrared_loader_app(void* p) {
+ UNUSED(p);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/main/lfrfid/125_10px.png b/applications/main/lfrfid/125_10px.png
new file mode 100644
index 000000000..ce01284a2
Binary files /dev/null and b/applications/main/lfrfid/125_10px.png differ
diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam
index 4a1498181..41d3b8fa7 100644
--- a/applications/main/lfrfid/application.fam
+++ b/applications/main/lfrfid/application.fam
@@ -8,18 +8,12 @@ App(
"gui",
"dialogs",
],
- provides=[
- "lfrfid_start",
- ],
+ # provides=[ "lfrfid_start", ],
icon="A_125khz_14",
stack_size=2 * 1024,
order=20,
-)
-
-App(
- appid="lfrfid_start",
- apptype=FlipperAppType.STARTUP,
- entry_point="lfrfid_on_system_start",
- requires=["lfrfid"],
- order=50,
+ # fap_category="Main",
+ # fap_icon="125_10px.png",
+ # fap_icon_assets="images",
+ # fap_libs=["assets"],
)
diff --git a/applications/main/lfrfid/images/125_10px.png b/applications/main/lfrfid/images/125_10px.png
new file mode 100644
index 000000000..ce01284a2
Binary files /dev/null and b/applications/main/lfrfid/images/125_10px.png differ
diff --git a/applications/main/lfrfid/images/ButtonRight_4x7.png b/applications/main/lfrfid/images/ButtonRight_4x7.png
new file mode 100644
index 000000000..8e1c74c1c
Binary files /dev/null and b/applications/main/lfrfid/images/ButtonRight_4x7.png differ
diff --git a/applications/main/lfrfid/images/DolphinCommon_56x48.png b/applications/main/lfrfid/images/DolphinCommon_56x48.png
new file mode 100644
index 000000000..089aaed83
Binary files /dev/null and b/applications/main/lfrfid/images/DolphinCommon_56x48.png differ
diff --git a/applications/main/lfrfid/images/DolphinMafia_115x62.png b/applications/main/lfrfid/images/DolphinMafia_115x62.png
new file mode 100644
index 000000000..53dffb4fa
Binary files /dev/null and b/applications/main/lfrfid/images/DolphinMafia_115x62.png differ
diff --git a/applications/main/lfrfid/images/DolphinNice_96x59.png b/applications/main/lfrfid/images/DolphinNice_96x59.png
new file mode 100644
index 000000000..b111196c7
Binary files /dev/null and b/applications/main/lfrfid/images/DolphinNice_96x59.png differ
diff --git a/applications/main/lfrfid/images/NFC_manual_60x50.png b/applications/main/lfrfid/images/NFC_manual_60x50.png
new file mode 100644
index 000000000..787c0bcfe
Binary files /dev/null and b/applications/main/lfrfid/images/NFC_manual_60x50.png differ
diff --git a/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png b/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png
new file mode 100644
index 000000000..ff967599c
Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png differ
diff --git a/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png b/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png
new file mode 100644
index 000000000..343b9f734
Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png differ
diff --git a/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png b/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png
new file mode 100644
index 000000000..341999109
Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png differ
diff --git a/applications/main/lfrfid/images/RFIDSmallChip_14x14.png b/applications/main/lfrfid/images/RFIDSmallChip_14x14.png
new file mode 100644
index 000000000..24219a548
Binary files /dev/null and b/applications/main/lfrfid/images/RFIDSmallChip_14x14.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png
new file mode 100644
index 000000000..a5dc239d8
Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png
new file mode 100644
index 000000000..162d8a8f4
Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png
new file mode 100644
index 000000000..5483e4734
Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png
new file mode 100644
index 000000000..ce2fbbd47
Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png
new file mode 100644
index 000000000..8b786c029
Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png differ
diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_rate b/applications/main/lfrfid/images/Round_loader_8x8/frame_rate
new file mode 100644
index 000000000..d8263ee98
--- /dev/null
+++ b/applications/main/lfrfid/images/Round_loader_8x8/frame_rate
@@ -0,0 +1 @@
+2
\ No newline at end of file
diff --git a/applications/main/lfrfid/images/SDQuestion_35x43.png b/applications/main/lfrfid/images/SDQuestion_35x43.png
new file mode 100644
index 000000000..9b9c9a58e
Binary files /dev/null and b/applications/main/lfrfid/images/SDQuestion_35x43.png differ
diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c
index b0f989374..d391c5e89 100644
--- a/applications/main/lfrfid/lfrfid.c
+++ b/applications/main/lfrfid/lfrfid.c
@@ -1,4 +1,5 @@
#include "lfrfid_i.h"
+#include
static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -31,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
}
static LfRfid* lfrfid_alloc() {
- LfRfid* lfrfid = malloc(sizeof(LfRfid));
+ LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773
lfrfid->storage = furi_record_open(RECORD_STORAGE);
lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);
@@ -182,12 +183,14 @@ int32_t lfrfid_app(void* p) {
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc);
+ DOLPHIN_DEED(DolphinDeedRfidEmulate);
} else {
furi_string_set(app->file_path, args);
lfrfid_load_key_data(app, app->file_path, true);
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedRfidEmulate);
}
} else {
@@ -311,4 +314,4 @@ void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context)
void lfrfid_text_input_callback(void* context) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext);
-}
\ No newline at end of file
+}
diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h
index 71917c0c2..201caa4f6 100644
--- a/applications/main/lfrfid/lfrfid_i.h
+++ b/applications/main/lfrfid/lfrfid_i.h
@@ -7,7 +7,6 @@
#include
#include
#include
-#include
#include
#include
@@ -33,6 +32,8 @@
#include
#include
+#include
+// #include
#define LFRFID_KEY_NAME_SIZE 22
#define LFRFID_TEXT_STORE_SIZE 40
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
index 2725982f0..dc3918994 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
@@ -1,12 +1,9 @@
#include "../lfrfid_i.h"
-#include
void lfrfid_scene_emulate_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
- DOLPHIN_DEED(DolphinDeedRfidEmulate);
-
popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop);
if(!furi_string_empty(app->file_name)) {
popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
index 39ae3c63b..ecca7cf11 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
@@ -1,4 +1,5 @@
#include "../lfrfid_i.h"
+#include
typedef enum {
SubmenuIndexASK,
@@ -57,10 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event)
if(event.event == SubmenuIndexASK) {
app->read_type = LFRFIDWorkerReadTypeASKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
+ DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true;
} else if(event.event == SubmenuIndexPSK) {
app->read_type = LFRFIDWorkerReadTypePSKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
+ DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true;
} else if(event.event == SubmenuIndexRAW) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c
index 4bdb215d1..5f1959728 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c
@@ -46,7 +46,6 @@ static void
void lfrfid_scene_read_on_enter(void* context) {
LfRfid* app = context;
- DOLPHIN_DEED(DolphinDeedRfidRead);
if(app->read_type == LFRFIDWorkerReadTypePSKOnly) {
lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly);
} else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) {
@@ -79,10 +78,10 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) {
consumed = true;
} else if(event.event == LfRfidEventReadDone) {
app->protocol_id = app->protocol_id_next;
- DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
notification_message(app->notifications, &sequence_success);
furi_string_reset(app->file_name);
scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess);
+ DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
consumed = true;
} else if(event.event == LfRfidEventReadStartPSK) {
if(app->read_type == LFRFIDWorkerReadTypeAuto) {
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c
index 7480304b6..081c47912 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c
@@ -1,4 +1,5 @@
#include "../lfrfid_i.h"
+#include
typedef enum {
SubmenuIndexSave,
@@ -43,6 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event)
consumed = true;
} else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedRfidEmulate);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c
index 6c5ea2f2d..11a687bdd 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c
@@ -1,5 +1,4 @@
#include "../lfrfid_i.h"
-#include
void lfrfid_scene_save_data_on_enter(void* context) {
LfRfid* app = context;
@@ -32,7 +31,6 @@ bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) {
consumed = true;
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size);
- DOLPHIN_DEED(DolphinDeedRfidAdd);
scene_manager_next_scene(scene_manager, LfRfidSceneSaveName);
scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1);
}
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c
index ca9a52de0..87e110f18 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c
@@ -1,5 +1,6 @@
#include
#include "../lfrfid_i.h"
+#include
void lfrfid_scene_save_name_on_enter(void* context) {
LfRfid* app = context;
@@ -55,6 +56,13 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(lfrfid_save_key(app)) {
scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess);
+ if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) {
+ // Nothing, do not count editing as saving
+ } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) {
+ DOLPHIN_DEED(DolphinDeedRfidAdd);
+ } else {
+ DOLPHIN_DEED(DolphinDeedRfidSave);
+ }
} else {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, LfRfidSceneReadKeyMenu);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
index e91ad04e3..52aefa848 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
@@ -1,5 +1,4 @@
#include "../lfrfid_i.h"
-#include
void lfrfid_scene_save_success_on_enter(void* context) {
LfRfid* app = context;
@@ -8,7 +7,6 @@ void lfrfid_scene_save_success_on_enter(void* context) {
// Clear state of data enter scene
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0);
- DOLPHIN_DEED(DolphinDeedRfidSave);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_context(popup, app);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
index 040b31f10..d3c3d389a 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c
@@ -1,4 +1,5 @@
#include "../lfrfid_i.h"
+#include
typedef enum {
SubmenuIndexEmulate,
@@ -42,6 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
+ DOLPHIN_DEED(DolphinDeedRfidEmulate);
consumed = true;
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
index d6d87f441..8e1c92dbb 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
@@ -1,4 +1,5 @@
#include "../lfrfid_i.h"
+#include
typedef enum {
SubmenuIndexRead,
@@ -47,6 +48,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
+ DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
furi_string_set(app->file_path, LFRFID_APP_FOLDER);
diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c
index 0d4db6178..4828e21a7 100644
--- a/applications/main/lfrfid/views/lfrfid_view_read.c
+++ b/applications/main/lfrfid/views/lfrfid_view_read.c
@@ -1,5 +1,7 @@
#include "lfrfid_view_read.h"
#include
+#include
+// #include
#define TEMP_STR_LEN 128
diff --git a/applications/main/lfrfid_loader/application.fam b/applications/main/lfrfid_loader/application.fam
new file mode 100644
index 000000000..947762085
--- /dev/null
+++ b/applications/main/lfrfid_loader/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="lfrfid_loader",
+ name="125 kHz RFID",
+ apptype=FlipperAppType.APP,
+ entry_point="lfrfid_loader_app",
+ requires=[
+ "gui",
+ "dialogs",
+ ],
+ stack_size=int(2 * 1024),
+ icon="A_125khz_14",
+ order=20,
+ link="/ext/apps/Main/lfrfid.fap",
+)
diff --git a/applications/main/lfrfid_loader/lfrfid_loader_app.c b/applications/main/lfrfid_loader/lfrfid_loader_app.c
new file mode 100644
index 000000000..ad6a96c64
--- /dev/null
+++ b/applications/main/lfrfid_loader/lfrfid_loader_app.c
@@ -0,0 +1,9 @@
+#include
+
+#define TAG "lfrfid_loader_app"
+
+int32_t lfrfid_loader_app(void* p) {
+ UNUSED(p);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/main/nfc/helpers/nfc_generators.c b/applications/main/nfc/helpers/nfc_generators.c
index 11083b9f0..5f0527c6a 100644
--- a/applications/main/nfc/helpers/nfc_generators.c
+++ b/applications/main/nfc/helpers/nfc_generators.c
@@ -314,7 +314,7 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) {
mful->version.storage_size = 0x15;
}
-static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) {
+void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) {
nfc_generate_common_start(data);
nfc_generate_mf_classic_common(data, uid_len, type);
@@ -337,6 +337,9 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas
}
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
}
+ // Set SAK to 18
+ data->nfc_data.sak = 0x18;
+
} else if(type == MfClassicType1k) {
// Set every block to 0xFF
for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) {
@@ -347,6 +350,8 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas
}
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
}
+ // Set SAK to 08
+ data->nfc_data.sak = 0x08;
}
mfc->type = type;
diff --git a/applications/main/nfc/helpers/nfc_generators.h b/applications/main/nfc/helpers/nfc_generators.h
index 10a05591b..362a19b1e 100644
--- a/applications/main/nfc/helpers/nfc_generators.h
+++ b/applications/main/nfc/helpers/nfc_generators.h
@@ -11,3 +11,5 @@ struct NfcGenerator {
};
extern const NfcGenerator* const nfc_generators[];
+
+void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type);
diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c
index 0b685f545..cff994baf 100644
--- a/applications/main/nfc/nfc.c
+++ b/applications/main/nfc/nfc.c
@@ -1,5 +1,6 @@
#include "nfc_i.h"
#include "furi_hal_nfc.h"
+#include
bool nfc_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -85,6 +86,13 @@ Nfc* nfc_alloc() {
nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box));
nfc->text_box_store = furi_string_alloc();
+ // Variable Item List
+ nfc->variable_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ nfc->view_dispatcher,
+ NfcViewVarItemList,
+ variable_item_list_get_view(nfc->variable_item_list));
+
// Custom Widget
nfc->widget = widget_alloc();
view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget));
@@ -115,7 +123,9 @@ void nfc_free(Nfc* nfc) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Save data in shadow file
- nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
+ if(furi_string_size(nfc->dev->load_path)) {
+ nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
+ }
}
if(nfc->rpc_ctx) {
rpc_system_app_send_exited(nfc->rpc_ctx);
@@ -155,6 +165,10 @@ void nfc_free(Nfc* nfc) {
text_box_free(nfc->text_box);
furi_string_free(nfc->text_box_store);
+ // Variable Item List
+ view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewVarItemList);
+ variable_item_list_free(nfc->variable_item_list);
+
// Custom Widget
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget);
widget_free(nfc->widget);
@@ -217,6 +231,13 @@ void nfc_blink_stop(Nfc* nfc) {
notification_message(nfc->notifications, &sequence_blink_stop);
}
+bool nfc_save_file(Nfc* nfc) {
+ furi_string_printf(
+ nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION);
+ bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
+ return file_saved;
+}
+
void nfc_show_loading_popup(void* context, bool show) {
Nfc* nfc = context;
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
@@ -275,12 +296,13 @@ int32_t nfc_app(void* p) {
if(nfc_device_load(nfc->dev, p, true)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
- } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
- scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
}
} else {
// Exit app
diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h
index fa5b54edc..0f2e51dab 100644
--- a/applications/main/nfc/nfc_i.h
+++ b/applications/main/nfc/nfc_i.h
@@ -7,6 +7,7 @@
#include
#include
+#include
#include
#include
#include
@@ -20,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
@@ -77,6 +79,7 @@ struct Nfc {
TextInput* text_input;
ByteInput* byte_input;
TextBox* text_box;
+ VariableItemList* variable_item_list;
Widget* widget;
DictAttack* dict_attack;
DetectReader* detect_reader;
@@ -92,6 +95,7 @@ typedef enum {
NfcViewTextInput,
NfcViewByteInput,
NfcViewTextBox,
+ NfcViewVarItemList,
NfcViewWidget,
NfcViewDictAttack,
NfcViewDetectReader,
@@ -113,4 +117,6 @@ void nfc_blink_detect_start(Nfc* nfc);
void nfc_blink_stop(Nfc* nfc);
+bool nfc_save_file(Nfc* nfc);
+
void nfc_show_loading_popup(void* context, bool show);
diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h
index a25850c84..0bebe35f3 100644
--- a/applications/main/nfc/scenes/nfc_scene_config.h
+++ b/applications/main/nfc/scenes/nfc_scene_config.h
@@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate)
ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult)
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
+ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto)
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess)
@@ -36,8 +37,22 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
+ADD_SCENE(nfc, mf_classic_write, MfClassicWrite)
+ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess)
+ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail)
+ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate)
+ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess)
+ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard)
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
ADD_SCENE(nfc, emv_menu, EmvMenu)
+ADD_SCENE(nfc, passport_read, PassportReadSuccess)
+ADD_SCENE(nfc, passport_read_auth, PassportReadAuthSuccess)
+ADD_SCENE(nfc, passport_menu, PassportMenu)
+ADD_SCENE(nfc, passport_auth, PassportAuth)
+ADD_SCENE(nfc, passport_auth_save_name, PassportAuthSaveName)
+ADD_SCENE(nfc, passport_date, PassportDate)
+ADD_SCENE(nfc, passport_docnr, PassportDocNr)
+ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo)
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
ADD_SCENE(nfc, device_info, DeviceInfo)
ADD_SCENE(nfc, delete, Delete)
@@ -54,3 +69,4 @@ ADD_SCENE(nfc, detect_reader, DetectReader)
ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo)
ADD_SCENE(nfc, mfkey_complete, MfkeyComplete)
ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)
+ADD_SCENE(nfc, read_card_type, ReadCardType)
diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c
index f0177f9c1..745946157 100644
--- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c
+++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
#define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U)
@@ -26,10 +25,14 @@ void nfc_scene_detect_reader_callback(void* context) {
void nfc_scene_detect_reader_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcDetectReader);
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
+ NfcDeviceData* dev_data = &nfc->dev->dev_data;
+ if(dev_data->nfc_data.uid_len) {
+ detect_reader_set_uid(
+ nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len);
+ }
// Store number of collected nonces in scene state
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0);
diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
index 8bb207960..5ddb60992 100644
--- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
+++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
#define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200)
@@ -59,7 +58,6 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
void nfc_scene_emulate_uid_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcEmulate);
// Setup Widget
nfc_scene_emulate_uid_widget_config(nfc, false);
diff --git a/applications/main/nfc/scenes/nfc_scene_emv_menu.c b/applications/main/nfc/scenes/nfc_scene_emv_menu.c
index eb1e10043..1da630fcf 100644
--- a/applications/main/nfc/scenes/nfc_scene_emv_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_emv_menu.c
@@ -1,6 +1,7 @@
#include "../nfc_i.h"
enum SubmenuIndex {
+ SubmenuIndexSave,
SubmenuIndexInfo,
};
@@ -14,6 +15,7 @@ void nfc_scene_emv_menu_on_enter(void* context) {
Nfc* nfc = context;
Submenu* submenu = nfc->submenu;
+ submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
@@ -26,7 +28,13 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubmenuIndexInfo) {
+ if(event.event == SubmenuIndexSave) {
+ nfc->dev->format = NfcDeviceSaveFormatBankCard;
+ // Clear device name
+ nfc_device_set_name(nfc->dev, "");
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+ consumed = true;
+ } else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
consumed = true;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c
index 6a0b32fad..005b76cb2 100644
--- a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c
@@ -1,6 +1,5 @@
#include "../nfc_i.h"
#include "../helpers/nfc_emv_parser.h"
-#include
void nfc_scene_emv_read_success_widget_callback(
GuiButtonType result,
@@ -15,7 +14,6 @@ void nfc_scene_emv_read_success_widget_callback(
void nfc_scene_emv_read_success_on_enter(void* context) {
Nfc* nfc = context;
EmvData* emv_data = &nfc->dev->dev_data.emv_data;
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup Custom Widget view
widget_add_button_element(
diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c
index b22dd42a1..3ce4f6de8 100644
--- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c
+++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c
@@ -29,8 +29,13 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
if(event.event == DialogExResultRight) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
} else if(event.event == DialogExResultLeft) {
- consumed = scene_manager_search_and_switch_to_previous_scene(
- nfc->scene_manager, NfcSceneStart);
+ if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadCardType)) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneReadCardType);
+ } else {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneStart);
+ }
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c
index e888e9d35..fc6021d73 100644
--- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c
+++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c
@@ -1,6 +1,7 @@
#include "../nfc_i.h"
enum SubmenuIndex {
+ SubmenuIndexReadCardType,
SubmenuIndexMfClassicKeys,
SubmenuIndexMfUltralightUnlock,
};
@@ -15,6 +16,12 @@ void nfc_scene_extra_actions_on_enter(void* context) {
Nfc* nfc = context;
Submenu* submenu = nfc->submenu;
+ submenu_add_item(
+ submenu,
+ "Read Specific Card Type",
+ SubmenuIndexReadCardType,
+ nfc_scene_extra_actions_submenu_callback,
+ nfc);
submenu_add_item(
submenu,
"Mifare Classic Keys",
@@ -44,9 +51,15 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
consumed = true;
} else if(event.event == SubmenuIndexMfUltralightUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
+ consumed = true;
+ } else if(event.event == SubmenuIndexReadCardType) {
+ scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0);
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType);
+ consumed = true;
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
}
+
return consumed;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c
index 693fdec20..374a933d1 100644
--- a/applications/main/nfc/scenes/nfc_scene_file_select.c
+++ b/applications/main/nfc/scenes/nfc_scene_file_select.c
@@ -5,6 +5,9 @@ void nfc_scene_file_select_on_enter(void* context) {
Nfc* nfc = context;
// Process file_select return
nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc);
+ if(!furi_string_size(nfc->dev->load_path)) {
+ furi_string_set_str(nfc->dev->load_path, NFC_APP_FOLDER);
+ }
if(nfc_file_select(nfc->dev)) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
index 1a6dab4e8..1004b3b26 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
+#include
#define TAG "NfcMfClassicDictAttack"
@@ -110,6 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
} else {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
}
} else if(event.event == NfcWorkerEventAborted) {
@@ -119,6 +121,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
} else {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
+ // Counting failed attempts too
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
}
} else if(event.event == NfcWorkerEventCardDetected) {
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
index 65639b2b4..68eda7040 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL)
#define NFC_MF_CLASSIC_DATA_CHANGED (1UL)
@@ -15,7 +14,6 @@ bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context)
void nfc_scene_mf_classic_emulate_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcEmulate);
// Setup view
Popup* popup = nfc->popup;
@@ -50,7 +48,10 @@ bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent even
NFC_MF_CLASSIC_DATA_CHANGED) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED);
- nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
+ // Save shadow file
+ if(furi_string_size(nfc->dev->load_path)) {
+ nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
+ }
}
consumed = false;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c
index 7874f6e09..e3335308f 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c
@@ -34,8 +34,6 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
- widget_add_button_element(
- nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc);
widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36);
if(user_dict_keys_total > 0) {
widget_add_button_element(
@@ -57,9 +55,6 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event)
if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
consumed = true;
- } else if(event.event == GuiButtonTypeLeft) {
- scene_manager_previous_scene(nfc->scene_manager);
- consumed = true;
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList);
consumed = true;
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c
index 2921d21c9..b122aa225 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
+#include
void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) {
Nfc* nfc = context;
@@ -36,6 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
} else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c
index 2cba04337..a5bb10ddf 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c
@@ -36,8 +36,6 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) {
- DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
-
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
@@ -49,6 +47,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
+ if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
+ } else {
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+ }
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_set_scene_state(
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c
index 0cdb86464..444c9a540 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_mf_classic_read_success_widget_callback(
GuiButtonType result,
@@ -18,8 +17,6 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
NfcDeviceData* dev_data = &nfc->dev->dev_data;
MfClassicData* mf_data = &dev_data->mf_classic_data;
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
-
// Setup view
Widget* widget = nfc->widget;
widget_add_button_element(
@@ -27,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
- FuriString* temp_str;
+ FuriString* temp_str = NULL;
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
} else {
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c
new file mode 100644
index 000000000..aacf77f77
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c
@@ -0,0 +1,98 @@
+#include "../nfc_i.h"
+#include
+
+enum {
+ NfcSceneMfClassicUpdateStateCardSearch,
+ NfcSceneMfClassicUpdateStateCardFound,
+};
+
+bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) {
+ furi_assert(context);
+
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
+
+ return true;
+}
+
+static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) {
+ Popup* popup = nfc->popup;
+ popup_reset(popup);
+ uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate);
+
+ if(state == NfcSceneMfClassicUpdateStateCardSearch) {
+ popup_set_text(
+ nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
+ popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
+ } else {
+ popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+ popup_set_icon(popup, 12, 23, &A_Loading_24);
+ }
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+void nfc_scene_mf_classic_update_on_enter(void* context) {
+ Nfc* nfc = context;
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
+ nfc_scene_mf_classic_update_setup_view(nfc);
+
+ // Setup and start worker
+ nfc_worker_start(
+ nfc->worker,
+ NfcWorkerStateMfClassicUpdate,
+ &nfc->dev->dev_data,
+ nfc_mf_classic_update_worker_callback,
+ nfc);
+ nfc_blink_emulate_start(nfc);
+}
+
+bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcWorkerEventSuccess) {
+ nfc_worker_stop(nfc->worker);
+ if(nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path))) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess);
+ } else {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
+ }
+ consumed = true;
+ } else if(event.event == NfcWorkerEventWrongCard) {
+ nfc_worker_stop(nfc->worker);
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventCardDetected) {
+ scene_manager_set_scene_state(
+ nfc->scene_manager,
+ NfcSceneMfClassicUpdate,
+ NfcSceneMfClassicUpdateStateCardFound);
+ nfc_scene_mf_classic_update_setup_view(nfc);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventNoCardDetected) {
+ scene_manager_set_scene_state(
+ nfc->scene_manager,
+ NfcSceneMfClassicUpdate,
+ NfcSceneMfClassicUpdateStateCardSearch);
+ nfc_scene_mf_classic_update_setup_view(nfc);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_update_on_exit(void* context) {
+ Nfc* nfc = context;
+ nfc_worker_stop(nfc->worker);
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
+ // Clear view
+ popup_reset(nfc->popup);
+
+ nfc_blink_stop(nfc);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c
new file mode 100644
index 000000000..fef8fd5e9
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c
@@ -0,0 +1,44 @@
+#include "../nfc_i.h"
+#include
+
+void nfc_scene_mf_classic_update_success_popup_callback(void* context) {
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+}
+
+void nfc_scene_mf_classic_update_success_on_enter(void* context) {
+ Nfc* nfc = context;
+ DOLPHIN_DEED(DolphinDeedNfcSave);
+
+ notification_message(nfc->notifications, &sequence_success);
+
+ Popup* popup = nfc->popup;
+ popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+ popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom);
+ popup_set_timeout(popup, 1500);
+ popup_set_context(popup, nfc);
+ popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback);
+ popup_enable_timeout(popup);
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventViewExit) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneFileSelect);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_update_success_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ popup_reset(nfc->popup);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c
new file mode 100644
index 000000000..3543cbc58
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c
@@ -0,0 +1,92 @@
+#include "../nfc_i.h"
+#include
+
+enum {
+ NfcSceneMfClassicWriteStateCardSearch,
+ NfcSceneMfClassicWriteStateCardFound,
+};
+
+bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) {
+ furi_assert(context);
+
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
+
+ return true;
+}
+
+static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) {
+ Popup* popup = nfc->popup;
+ popup_reset(popup);
+ uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite);
+
+ if(state == NfcSceneMfClassicWriteStateCardSearch) {
+ popup_set_text(
+ nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
+ popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
+ } else {
+ popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+ popup_set_icon(popup, 12, 23, &A_Loading_24);
+ }
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+void nfc_scene_mf_classic_write_on_enter(void* context) {
+ Nfc* nfc = context;
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
+ nfc_scene_mf_classic_write_setup_view(nfc);
+
+ // Setup and start worker
+ nfc_worker_start(
+ nfc->worker,
+ NfcWorkerStateMfClassicWrite,
+ &nfc->dev->dev_data,
+ nfc_mf_classic_write_worker_callback,
+ nfc);
+ nfc_blink_emulate_start(nfc);
+}
+
+bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcWorkerEventSuccess) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventFail) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventWrongCard) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventCardDetected) {
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound);
+ nfc_scene_mf_classic_write_setup_view(nfc);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventNoCardDetected) {
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
+ nfc_scene_mf_classic_write_setup_view(nfc);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_write_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ nfc_worker_stop(nfc->worker);
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
+ // Clear view
+ popup_reset(nfc->popup);
+
+ nfc_blink_stop(nfc);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c
new file mode 100644
index 000000000..aeea6eef0
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c
@@ -0,0 +1,58 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_write_fail_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ Nfc* nfc = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+ }
+}
+
+void nfc_scene_mf_classic_write_fail_on_enter(void* context) {
+ Nfc* nfc = context;
+ Widget* widget = nfc->widget;
+
+ notification_message(nfc->notifications, &sequence_error);
+
+ widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
+ widget_add_string_element(
+ widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
+ widget_add_string_multiline_element(
+ widget,
+ 7,
+ 17,
+ AlignLeft,
+ AlignTop,
+ FontSecondary,
+ "Not all sectors\nwere written\ncorrectly.");
+
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc);
+
+ // Setup and start worker
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeLeft) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneFileSelect);
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneSavedMenu);
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_write_fail_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ widget_reset(nfc->widget);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c
new file mode 100644
index 000000000..2f2a3beb1
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c
@@ -0,0 +1,44 @@
+#include "../nfc_i.h"
+#include
+
+void nfc_scene_mf_classic_write_success_popup_callback(void* context) {
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+}
+
+void nfc_scene_mf_classic_write_success_on_enter(void* context) {
+ Nfc* nfc = context;
+ DOLPHIN_DEED(DolphinDeedNfcSave);
+
+ notification_message(nfc->notifications, &sequence_success);
+
+ Popup* popup = nfc->popup;
+ popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+ popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
+ popup_set_timeout(popup, 1500);
+ popup_set_context(popup, nfc);
+ popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback);
+ popup_enable_timeout(popup);
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventViewExit) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneFileSelect);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_write_success_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ popup_reset(nfc->popup);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c
new file mode 100644
index 000000000..2c56270e3
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c
@@ -0,0 +1,53 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_wrong_card_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ Nfc* nfc = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+ }
+}
+
+void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
+ Nfc* nfc = context;
+ Widget* widget = nfc->widget;
+
+ notification_message(nfc->notifications, &sequence_error);
+
+ widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
+ widget_add_string_element(
+ widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
+ widget_add_string_multiline_element(
+ widget,
+ 4,
+ 17,
+ AlignLeft,
+ AlignTop,
+ FontSecondary,
+ "Data management\nis only possible\nwith initial card");
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc);
+
+ // Setup and start worker
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeLeft) {
+ consumed = scene_manager_previous_scene(nfc->scene_manager);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_classic_wrong_card_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ widget_reset(nfc->widget);
+}
\ No newline at end of file
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c
index 1e2f2d2f2..bee63d775 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
+#include
enum SubmenuIndex {
SubmenuIndexSave,
@@ -48,6 +49,11 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event)
consumed = true;
} else if(event.event == SubmenuIndexEmulateUid) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
+ if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
+ } else {
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+ }
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
index cea334a25..1505064a4 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
@@ -1,9 +1,11 @@
#include "../nfc_i.h"
#include
-#include
#define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200)
+#define NFC_MF_UL_DATA_NOT_CHANGED (0UL)
+#define NFC_MF_UL_DATA_CHANGED (1UL)
+
enum {
// View states
NfcSceneMfUltralightEmulateStateWidget,
@@ -87,7 +89,6 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
- DOLPHIN_DEED(DolphinDeedNfcEmulate);
// Setup Widget
nfc_scene_mf_ultralight_emulate_widget_config(nfc, false);
@@ -119,37 +120,18 @@ bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent e
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
bool consumed = false;
- if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == NfcCustomEventWorkerExit) {
- if(state & NfcSceneMfUltralightEmulateStateAuthAttempted) {
- if(!(state & NfcSceneMfUltralightEmulateStateLogButtonShown)) {
- // Add log button to widget not already showing
- nfc_scene_mf_ultralight_emulate_widget_config(nfc, true);
- state |= NfcSceneMfUltralightEmulateStateLogButtonShown;
- }
- // The text box update logic is handled in the worker callback
- state &= ~NfcSceneMfUltralightEmulateStateAuthAttempted;
- scene_manager_set_scene_state(
- nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
- consumed = true;
+ if(event.type == SceneManagerEventTypeBack) {
+ // Stop worker
+ nfc_worker_stop(nfc->worker);
+ // Check if data changed and save in shadow file
+ if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) ==
+ NFC_MF_UL_DATA_CHANGED) {
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED);
+ // Save shadow file
+ if(furi_string_size(nfc->dev->load_path)) {
+ nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
}
- } else if(
- event.event == GuiButtonTypeCenter && (state & NfcSceneMfUltralightEmulateStateMax) ==
- NfcSceneMfUltralightEmulateStateWidget) {
- view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
- state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
- NfcSceneMfUltralightEmulateStateTextBox;
- scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
- consumed = true;
- }
- } else if(event.type == SceneManagerEventTypeBack) {
- if((state & NfcSceneMfUltralightEmulateStateMax) ==
- NfcSceneMfUltralightEmulateStateTextBox) {
- view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
- state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
- NfcSceneMfUltralightEmulateStateWidget;
- scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
- consumed = true;
}
}
return consumed;
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c
index ba9f22338..ddf30c54a 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c
@@ -1,7 +1,9 @@
#include "../nfc_i.h"
+#include
enum SubmenuIndex {
- SubmenuIndexUnlock,
+ SubmenuIndexUnlockByReader,
+ SubmenuIndexUnlockByPassword,
SubmenuIndexSave,
SubmenuIndexEmulate,
SubmenuIndexInfo,
@@ -18,11 +20,17 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu;
MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
- if(data->data_read != data->data_size) {
+ if(!mf_ul_is_full_capture(data)) {
+ submenu_add_item(
+ submenu,
+ "Unlock With Reader",
+ SubmenuIndexUnlockByReader,
+ nfc_scene_mf_ultralight_menu_submenu_callback,
+ nfc);
submenu_add_item(
submenu,
"Unlock With Password",
- SubmenuIndexUnlock,
+ SubmenuIndexUnlockByPassword,
nfc_scene_mf_ultralight_menu_submenu_callback,
nfc);
}
@@ -56,8 +64,16 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even
consumed = true;
} else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
+ if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
+ } else {
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+ }
consumed = true;
- } else if(event.event == SubmenuIndexUnlock) {
+ } else if(event.event == SubmenuIndexUnlockByReader) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
+ consumed = true;
+ } else if(event.event == SubmenuIndexUnlockByPassword) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
index fcea79ca2..22a9d51b5 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
typedef enum {
NfcSceneMfUlReadStateIdle,
@@ -51,7 +50,6 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcRead);
nfc_device_clear(nfc->dev);
// Setup view
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c
index 5a690a213..ac2eea182 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_mf_ultralight_read_auth_result_widget_callback(
GuiButtonType result,
@@ -20,16 +19,19 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data);
Widget* widget = nfc->widget;
+ const char* title;
FuriString* temp_str;
temp_str = furi_string_alloc();
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
- widget_add_string_element(
- widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!");
+ if(mf_ul_data->auth_success)
+ title = "All pages are unlocked!";
+ else
+ title = "All unlocked but failed auth!";
} else {
- widget_add_string_element(
- widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!");
+ title = "Not all pages unlocked!";
}
+ widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title);
furi_string_set(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
@@ -37,7 +39,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
widget_add_string_element(
widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
if(mf_ul_data->auth_success) {
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
furi_string_printf(
temp_str,
"Password: %02X %02X %02X %02X",
@@ -54,8 +55,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
config_pages->auth_data.pack.raw[1]);
widget_add_string_element(
widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
- } else {
- DOLPHIN_DEED(DolphinDeedNfcMfulError);
}
furi_string_printf(
temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
index 77034ea80..cb5ccd6e8 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_mf_ultralight_read_success_widget_callback(
GuiButtonType result,
@@ -14,7 +13,6 @@ void nfc_scene_mf_ultralight_read_success_widget_callback(
void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup widget view
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
@@ -33,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
nfc_scene_mf_ultralight_read_success_widget_callback,
nfc);
- FuriString* temp_str;
+ FuriString* temp_str = NULL;
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
} else {
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c
new file mode 100644
index 000000000..70a06091f
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c
@@ -0,0 +1,56 @@
+#include "../nfc_i.h"
+
+bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
+ return true;
+}
+
+void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) {
+ Nfc* nfc = context;
+
+ // Setup view
+ popup_set_text(nfc->popup, "Touch the reader", 44, 31, AlignLeft, AlignCenter);
+ popup_set_icon(nfc->popup, 0, 16, &I_Tap_reader_36x38);
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+
+ // Start worker
+ nfc_worker_start(
+ nfc->worker,
+ NfcWorkerStateMfUltralightEmulate,
+ &nfc->dev->dev_data,
+ nfc_scene_mf_ultralight_unlock_auto_worker_callback,
+ nfc);
+
+ nfc_blink_emulate_start(nfc);
+}
+
+bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if((event.event == NfcWorkerEventMfUltralightPwdAuth)) {
+ MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
+ memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw));
+ nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual;
+ nfc_worker_stop(nfc->worker);
+ notification_message(nfc->notifications, &sequence_success);
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Stop worker
+ nfc_worker_stop(nfc->worker);
+ // Clear view
+ popup_reset(nfc->popup);
+
+ nfc_blink_stop(nfc);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c
index 95f1edb62..979aa8eda 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c
@@ -58,7 +58,8 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
consumed = true;
}
- scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
+ scene_manager_set_scene_state(
+ nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event);
}
return consumed;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c
index 708f908cd..0b88043e6 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
+#include
void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) {
Nfc* nfc = context;
@@ -30,6 +31,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
+ DOLPHIN_DEED(DolphinDeedNfcRead);
consumed = true;
}
}
diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
index 8f33972e0..fa73201d0 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
@@ -32,6 +32,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
// Set tag type
if(protocol == NfcDeviceProtocolEMV) {
furi_string_cat_printf(temp_str, "\e#EMV Bank Card\n");
+ } else if(protocol == NfcDeviceProtocolMRTD) {
+ furi_string_cat_printf(temp_str, "\e#Passport/ID\n");
} else if(protocol == NfcDeviceProtocolMifareUl) {
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true));
@@ -87,6 +89,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4);
if(data->data_size > data->data_read) {
furi_string_cat_printf(temp_str, "\nPassword-protected");
+ } else if(data->auth_success) {
+ MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data);
+ furi_string_cat_printf(
+ temp_str,
+ "\nPassword: %02X %02X %02X %02X",
+ config_pages->auth_data.pwd.raw[0],
+ config_pages->auth_data.pwd.raw[1],
+ config_pages->auth_data.pwd.raw[2],
+ config_pages->auth_data.pwd.raw[3]);
+ furi_string_cat_printf(
+ temp_str,
+ "\nPACK: %02X %02X",
+ config_pages->auth_data.pack.raw[0],
+ config_pages->auth_data.pack.raw[1]);
}
} else if(protocol == NfcDeviceProtocolMifareClassic) {
MfClassicData* data = &dev_data->mf_classic_data;
@@ -131,4 +147,4 @@ void nfc_scene_nfc_data_info_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
-}
\ No newline at end of file
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c
index 00d0d943d..30f63945c 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
+#include
enum SubmenuIndex {
SubmenuIndexSaveUid,
@@ -41,6 +42,11 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) {
consumed = true;
} else if(event.event == SubmenuIndexEmulateUid) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
+ if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
+ } else {
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+ }
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c
index 2ea7c9921..a38f31a98 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_nfca_read_success_widget_callback(
GuiButtonType result,
@@ -16,8 +15,6 @@ void nfc_scene_nfca_read_success_widget_callback(
void nfc_scene_nfca_read_success_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
-
// Setup view
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
Widget* widget = nfc->widget;
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c
new file mode 100644
index 000000000..c78c8b70e
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c
@@ -0,0 +1,194 @@
+#include "../nfc_i.h"
+
+#define TAG "PassportAuth"
+
+#define MRTD_AUTH_METHOD_COUNT 4
+// Must match MrtdAuthMethod size (lib/nfc/protocols/mrtd_helpers.h)
+
+typedef enum {
+ NfcScenePassportAuthSelectDob,
+ NfcScenePassportAuthSelectDoe,
+ NfcScenePassportAuthSelectDocNr,
+ NfcScenePassportAuthSelectMethod,
+ NfcScenePassportAuthSelectAuth,
+ NfcScenePassportAuthSelectSave,
+ NfcScenePassportAuthSelectLoad,
+} NfcScenePassportAuthSelect;
+
+void nfc_scene_passport_auth_var_list_enter_callback(void* context, uint32_t index) {
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_passport_auth_method_changed(VariableItem* item) {
+ Nfc* nfc = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ nfc->dev->dev_data.mrtd_data.auth.method = index;
+ variable_item_set_current_value_text(item, mrtd_auth_method_string(index));
+}
+
+bool nfc_scene_passport_auth_load(Nfc* nfc) {
+ const DialogsFileBrowserOptions browser_options = {
+ .extension = MRTD_APP_EXTENSION,
+ .skip_assets = true,
+ .icon = &I_u2f_10px,
+ .hide_ext = true,
+ .item_loader_callback = NULL,
+ .item_loader_context = NULL,
+ };
+
+ FuriString* mrtd_app_folder;
+ mrtd_app_folder = furi_string_alloc_set(MRTD_APP_FOLDER);
+
+ FuriString* file_path;
+ file_path = furi_string_alloc();
+
+ bool res =
+ dialog_file_browser_show(nfc->dev->dialogs, file_path, mrtd_app_folder, &browser_options);
+
+ furi_string_free(mrtd_app_folder);
+
+ if(res) {
+ mrtd_auth_params_load(
+ nfc->dev->storage,
+ nfc->dev->dialogs,
+ &nfc->dev->dev_data.mrtd_data.auth,
+ furi_string_get_cstr(file_path),
+ true);
+
+ nfc_scene_passport_auth_on_enter(nfc);
+ variable_item_list_set_selected_item(
+ nfc->variable_item_list, NfcScenePassportAuthSelectAuth);
+ }
+
+ return res;
+}
+
+void nfc_scene_passport_auth_on_enter(void* context) {
+ Nfc* nfc = context;
+ MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
+
+ // By entering the Auth menu, we default to Auth: Any
+ MrtdAuthMethod* auth_method = &mrtd_data->auth.method;
+ if(*auth_method == MrtdAuthMethodNone) {
+ *auth_method = MrtdAuthMethodAny;
+ }
+
+ VariableItemList* variable_item_list = nfc->variable_item_list;
+ variable_item_list_reset(variable_item_list);
+
+ VariableItem* item;
+ uint8_t value_index;
+
+ const size_t temp_str_size = 15;
+ char temp_str[temp_str_size];
+ snprintf(
+ temp_str,
+ temp_str_size,
+ "%02u%02u%02u",
+ mrtd_data->auth.birth_date.year,
+ mrtd_data->auth.birth_date.month,
+ mrtd_data->auth.birth_date.day);
+
+ item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL);
+ variable_item_set_current_value_text(item, temp_str);
+
+ snprintf(
+ temp_str,
+ temp_str_size,
+ "%02u%02u%02u",
+ mrtd_data->auth.expiry_date.year,
+ mrtd_data->auth.expiry_date.month,
+ mrtd_data->auth.expiry_date.day);
+
+ item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL);
+ variable_item_set_current_value_text(item, temp_str);
+
+ item = variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL);
+
+ strncpy(temp_str, mrtd_data->auth.doc_number, temp_str_size);
+ temp_str[temp_str_size] = '\x00';
+ if(strlen(temp_str) > 8) {
+ temp_str[8] = '.';
+ temp_str[9] = '.';
+ temp_str[10] = '.';
+ temp_str[11] = '\x00';
+ }
+ variable_item_set_current_value_text(item, temp_str);
+
+ item = variable_item_list_add(
+ variable_item_list,
+ "Method",
+ MRTD_AUTH_METHOD_COUNT,
+ nfc_scene_passport_auth_method_changed,
+ nfc);
+
+ value_index = *auth_method;
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, mrtd_auth_method_string(value_index));
+
+ variable_item_list_add(variable_item_list, "Authenticate and read", 1, NULL, NULL);
+
+ variable_item_list_add(variable_item_list, "Save parameters", 1, NULL, NULL);
+ variable_item_list_add(variable_item_list, "Load parameters", 1, NULL, NULL);
+
+ variable_item_list_set_enter_callback(
+ variable_item_list, nfc_scene_passport_auth_var_list_enter_callback, nfc);
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewVarItemList);
+}
+
+bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ FURI_LOG_D(TAG, "event.event: %ld", event.event);
+ switch(event.event) {
+ case NfcScenePassportAuthSelectLoad:
+ nfc_scene_passport_auth_load(nfc);
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectDob:
+ scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0);
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate);
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectDoe:
+ scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 1);
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate);
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectDocNr:
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDocNr);
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectMethod:
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectAuth:
+ if(nfc->dev->dev_data.mrtd_data.auth.method == MrtdAuthMethodPace) {
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPaceTodo);
+ } else {
+ nfc_device_clear(nfc->dev);
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ }
+ consumed = true;
+ break;
+ case NfcScenePassportAuthSelectSave:
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuthSaveName);
+ consumed = true;
+ break;
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed = scene_manager_previous_scene(nfc->scene_manager);
+ }
+
+ return consumed;
+}
+
+void nfc_scene_passport_auth_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ variable_item_list_reset(nfc->variable_item_list);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c
new file mode 100644
index 000000000..65fee0bcf
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c
@@ -0,0 +1,82 @@
+#include "../nfc_i.h"
+#include
+#include
+#include
+
+void nfc_scene_passport_auth_save_name_text_input_callback(void* context) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
+}
+
+void nfc_scene_passport_auth_save_name_on_enter(void* context) {
+ Nfc* nfc = context;
+
+ MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
+
+ // Setup view
+ TextInput* text_input = nfc->text_input;
+ bool docnr_empty = false;
+ if(!strcmp(mrtd_data->auth.doc_number, "")) {
+ set_random_name(nfc->text_store, sizeof(nfc->text_store));
+ docnr_empty = true;
+ } else {
+ nfc_text_store_set(nfc, mrtd_data->auth.doc_number);
+ }
+ text_input_set_header_text(text_input, "Name the parameters");
+ text_input_set_result_callback(
+ text_input,
+ nfc_scene_passport_auth_save_name_text_input_callback,
+ nfc,
+ nfc->text_store,
+ NFC_DEV_NAME_MAX_LEN,
+ docnr_empty);
+
+ FuriString* folder_path;
+ folder_path = furi_string_alloc();
+
+ if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) {
+ path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path);
+ } else {
+ furi_string_set(folder_path, NFC_APP_FOLDER);
+ }
+
+ ValidatorIsFile* validator_is_file =
+ validator_is_file_alloc_init(furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, NULL);
+ text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
+
+ furi_string_free(folder_path);
+}
+
+bool nfc_scene_passport_auth_save_name_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventTextInputDone) {
+ if(mrtd_auth_params_save(
+ nfc->dev->storage, nfc->dev->dialogs, &mrtd_data->auth, nfc->text_store)) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
+ consumed = true;
+ } else {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcSceneStart);
+ }
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_auth_save_name_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ void* validator_context = text_input_get_validator_callback_context(nfc->text_input);
+ text_input_set_validator(nfc->text_input, NULL, NULL);
+ validator_is_file_free(validator_context);
+
+ text_input_reset(nfc->text_input);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_date.c b/applications/main/nfc/scenes/nfc_scene_passport_date.c
new file mode 100644
index 000000000..4dc174326
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_date.c
@@ -0,0 +1,120 @@
+#include "../nfc_i.h"
+#include "m-string.h"
+#include
+
+#define TAG "PassportDate"
+
+#define DATE_LENGTH 6
+
+//TODO: use types in .h file? also in nfc_scene_passport_bac.c
+#define NFC_PASSPORT_DATE_BIRTH 0
+#define NFC_PASSPORT_DATE_EXPIRY 1
+
+void nfc_scene_passport_date_text_input_callback(void* context) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
+}
+
+void nfc_scene_passport_date_on_enter(void* context) {
+ Nfc* nfc = context;
+
+ MrtdDate date_value;
+
+ uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate);
+
+ //TODO: numbers only
+ TextInput* text_input = nfc->text_input;
+
+ switch(date_type) {
+ case NFC_PASSPORT_DATE_BIRTH:
+ text_input_set_header_text(text_input, "Birth Date");
+ date_value = nfc->dev->dev_data.mrtd_data.auth.birth_date;
+ break;
+ case NFC_PASSPORT_DATE_EXPIRY:
+ text_input_set_header_text(text_input, "Expiry Date");
+ date_value = nfc->dev->dev_data.mrtd_data.auth.expiry_date;
+ break;
+ }
+
+ bool date_empty = false;
+ if(date_value.year == 0 || date_value.month == 0 || date_value.day == 0 ||
+ date_value.year > 100 || date_value.month > 13 || date_value.day > 31) {
+ nfc_text_store_set(nfc, "YYMMDD");
+ date_empty = true;
+ } else {
+ char temp_str[10];
+ snprintf(temp_str, 10, "%02u%02u%02u", date_value.year, date_value.month, date_value.day);
+
+ memcpy(nfc->text_store, temp_str, DATE_LENGTH);
+ nfc->text_store[DATE_LENGTH] = '\x00';
+ }
+
+ text_input_set_result_callback(
+ text_input,
+ nfc_scene_passport_date_text_input_callback,
+ nfc,
+ nfc->text_store,
+ DATE_LENGTH + 1, // incl. '\x00'
+ date_empty); // Use as template
+
+ //TODO: add validator for valid date (YYMMDD)
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
+}
+
+bool nfc_scene_passport_date_save(Nfc* nfc) {
+ int year;
+ int month;
+ int day;
+ int ret = sscanf(nfc->text_store, "%02d%02d%02d", &year, &month, &day);
+
+ if(ret != 3) {
+ FURI_LOG_E(TAG, "Invalid date entered (YYMMDD): %s", nfc->text_store);
+ return false;
+ }
+
+ MrtdDate date_value;
+ date_value.year = year;
+ date_value.month = month;
+ date_value.day = day;
+
+ uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate);
+
+ //TODO: use types in .h file? also in nfc_scene_passport_bac.c
+ switch(date_type) {
+ case NFC_PASSPORT_DATE_BIRTH:
+ nfc->dev->dev_data.mrtd_data.auth.birth_date = date_value;
+ break;
+ case NFC_PASSPORT_DATE_EXPIRY:
+ nfc->dev->dev_data.mrtd_data.auth.expiry_date = date_value;
+ break;
+ }
+
+ return true;
+}
+
+bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventTextInputDone) {
+ nfc_scene_passport_date_save(nfc);
+ //TODO: handle invalid date (returned false)
+
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcScenePassportAuth);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_date_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ // TODO: clear validator
+
+ text_input_reset(nfc->text_input);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_docnr.c b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c
new file mode 100644
index 000000000..109415759
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c
@@ -0,0 +1,67 @@
+#include "../nfc_i.h"
+#include "m-string.h"
+#include
+
+#define TAG "PassportDocnr"
+
+void nfc_scene_passport_docnr_text_input_callback(void* context) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
+}
+
+void nfc_scene_passport_docnr_on_enter(void* context) {
+ Nfc* nfc = context;
+
+ TextInput* text_input = nfc->text_input;
+ text_input_set_header_text(text_input, "Document Nr.");
+
+ char* docnr = nfc->dev->dev_data.mrtd_data.auth.doc_number;
+ bool docnr_empty = false;
+
+ if(*docnr) {
+ nfc_text_store_set(nfc, docnr);
+ docnr_empty = false;
+ } else {
+ nfc_text_store_set(nfc, "PA7HJ34M8");
+ docnr_empty = true;
+ }
+
+ text_input_set_result_callback(
+ text_input,
+ nfc_scene_passport_docnr_text_input_callback,
+ nfc,
+ nfc->text_store,
+ MRTD_DOCNR_MAX_LENGTH, // incl. '\x00'
+ docnr_empty); // Use as template
+
+ //TODO: add validator?
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
+}
+
+bool nfc_scene_passport_docnr_save(Nfc* nfc) {
+ strncpy(nfc->dev->dev_data.mrtd_data.auth.doc_number, nfc->text_store, MRTD_DOCNR_MAX_LENGTH);
+ return true;
+}
+
+bool nfc_scene_passport_docnr_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventTextInputDone) {
+ nfc_scene_passport_docnr_save(nfc);
+
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcScenePassportAuth);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_docnr_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ text_input_reset(nfc->text_input);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_menu.c b/applications/main/nfc/scenes/nfc_scene_passport_menu.c
new file mode 100644
index 000000000..27febcb18
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_menu.c
@@ -0,0 +1,57 @@
+#include "../nfc_i.h"
+
+enum SubmenuIndex {
+ SubmenuIndexSave,
+ SubmenuIndexInfo,
+};
+
+void nfc_scene_passport_menu_submenu_callback(void* context, uint32_t index) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_passport_menu_on_enter(void* context) {
+ Nfc* nfc = context;
+ Submenu* submenu = nfc->submenu;
+
+ submenu_add_item(
+ submenu, "Save", SubmenuIndexSave, nfc_scene_passport_menu_submenu_callback, nfc);
+ submenu_add_item(
+ submenu, "Info", SubmenuIndexInfo, nfc_scene_passport_menu_submenu_callback, nfc);
+ submenu_set_selected_item(
+ nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportMenu));
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
+}
+
+bool nfc_scene_passport_menu_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubmenuIndexSave) {
+ //TODO: save more than just UID
+ nfc->dev->format = NfcDeviceSaveFormatUid;
+ // Clear device name
+ nfc_device_set_name(nfc->dev, "");
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+ consumed = true;
+ } else if(event.event == SubmenuIndexInfo) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+ consumed = true;
+ }
+ scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportMenu, event.event);
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed = scene_manager_previous_scene(nfc->scene_manager);
+ }
+
+ return consumed;
+}
+
+void nfc_scene_passport_menu_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ submenu_reset(nfc->submenu);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c
new file mode 100644
index 000000000..c6d7d7f6c
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c
@@ -0,0 +1,40 @@
+#include "../nfc_i.h"
+
+void nfc_scene_passport_pace_todo_popup_callback(void* context) {
+ Nfc* nfc = context;
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
+}
+
+void nfc_scene_passport_pace_todo_on_enter(void* context) {
+ Nfc* nfc = context;
+
+ // Setup view
+ Popup* popup = nfc->popup;
+ popup_set_icon(popup, 64, 16, &I_DolphinCommon_56x48);
+ popup_set_header(popup, "PACE not yet implemented", 4, 4, AlignLeft, AlignTop);
+ popup_set_timeout(popup, 2000);
+ popup_set_context(popup, nfc);
+ popup_set_callback(popup, nfc_scene_passport_pace_todo_popup_callback);
+ popup_enable_timeout(popup);
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+bool nfc_scene_passport_pace_todo_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == NfcCustomEventViewExit) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcScenePassportAuth);
+ }
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_pace_todo_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ popup_reset(nfc->popup);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c
new file mode 100644
index 000000000..74f173b52
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c
@@ -0,0 +1,90 @@
+#include "../nfc_i.h"
+#include
+
+void nfc_scene_passport_read_widget_callback(GuiButtonType result, InputType type, void* context) {
+ Nfc* nfc = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+ }
+}
+
+void nfc_scene_passport_read_on_enter(void* context) {
+ Nfc* nfc = context;
+ FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
+ MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
+
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
+
+ Widget* widget = nfc->widget;
+
+ // Setup Custom Widget view
+ FuriString* temp_str;
+ temp_str = furi_string_alloc();
+ furi_string_set(temp_str, "\e#Passport\n");
+ char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3';
+
+ char nfc_type;
+ switch(data->type) {
+ case FuriHalNfcTypeA:
+ nfc_type = 'A';
+ break;
+ case FuriHalNfcTypeB:
+ nfc_type = 'B';
+ break;
+ default:
+ nfc_type = '?';
+ break;
+ }
+ furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-%c)\n", iso_type, nfc_type);
+ furi_string_cat_printf(temp_str, "UID:");
+ for(size_t i = 0; i < data->uid_len; i++) {
+ furi_string_cat_printf(temp_str, " %02X", data->uid[i]);
+ }
+ furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]);
+ furi_string_cat_printf(temp_str, " SAK: %02X\n", data->sak);
+
+ if(mrtd_data->auth.method != MrtdAuthMethodNone && !mrtd_data->auth_success) {
+ furi_string_cat_printf(temp_str, "Auth failed. Wrong params?");
+ }
+
+ widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
+ furi_string_free(temp_str);
+
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_widget_callback, nfc);
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_widget_callback, nfc);
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_widget_callback, nfc);
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_passport_read_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeLeft) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
+ consumed = true;
+ } else if(event.event == GuiButtonTypeCenter) {
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth);
+ consumed = true;
+ } else if(event.event == GuiButtonTypeRight) {
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu);
+ consumed = true;
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
+ consumed = true;
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_read_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ widget_reset(nfc->widget);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c
new file mode 100644
index 000000000..31a9e8ce1
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c
@@ -0,0 +1,142 @@
+#include "../nfc_i.h"
+#include
+
+const char months[13][4] = {
+ "---",
+ "JAN",
+ "FEB",
+ "MAR",
+ "APR",
+ "MAY",
+ "JUN",
+ "JUL",
+ "AUG",
+ "SEP",
+ "OCT",
+ "NOV",
+ "DEC",
+};
+
+void nfc_scene_passport_read_auth_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ Nfc* nfc = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+ }
+}
+
+void nfc_scene_passport_read_auth_on_enter(void* context) {
+ Nfc* nfc = context;
+ MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data;
+
+ Widget* widget = nfc->widget;
+
+ // Setup Custom Widget view
+ FuriString* temp_str;
+ temp_str = furi_string_alloc();
+ furi_string_set(temp_str, "\e#Passport\n");
+ furi_string_cat_printf(
+ temp_str, "Auth.method: %s\n", mrtd_auth_method_string(mrtd_data->auth_method_used));
+ // TODO: indicate BAC / PACE used
+
+ uint16_t lds_version = mrtd_data->files.EF_COM.lds_version;
+ furi_string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version / 100, lds_version % 100);
+
+ uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version;
+ furi_string_cat_printf(
+ temp_str,
+ "Unicode version: %d.%d.%d\n",
+ (uint8_t)(unicode_version / 10000),
+ (uint8_t)(unicode_version / 100 % 100),
+ (uint8_t)(unicode_version % 100));
+
+ furi_string_cat_printf(temp_str, "Avail.files: ");
+ for(size_t i = 0; i < MAX_EFCOM_TAGS; ++i) {
+ uint8_t tag = mrtd_data->files.EF_COM.tag_list[i];
+ const EFFile* file = mrtd_tag_to_file(tag);
+ if(file->tag) {
+ if(i > 0) furi_string_cat_printf(temp_str, ", ");
+ furi_string_cat_printf(temp_str, "%s", file->name);
+ }
+ }
+ furi_string_cat_printf(temp_str, "\n");
+
+ EF_DIR_contents* EF_DIR = &mrtd_data->files.EF_DIR;
+ if(EF_DIR->applications_count > 0) {
+ furi_string_cat_printf(temp_str, "Apps:\n");
+ for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) {
+ for(uint8_t n = 0; n < sizeof(AIDValue); ++n) {
+ furi_string_cat_printf(temp_str, "%02X ", EF_DIR->applications[i][n]);
+ }
+ furi_string_cat_printf(temp_str, "\n");
+ }
+ }
+
+ EF_DG1_contents* DG1 = &mrtd_data->files.DG1;
+ furi_string_cat_printf(temp_str, "\e#DG1\n");
+ furi_string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype);
+ furi_string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state);
+ furi_string_cat_printf(temp_str, "Name: %s\n", DG1->name);
+ furi_string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr);
+ furi_string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality);
+ furi_string_cat_printf(
+ temp_str,
+ "Birth Date: %02d %s %02d\n",
+ DG1->birth_date.day,
+ months[DG1->birth_date.month],
+ DG1->birth_date.year);
+ furi_string_cat_printf(temp_str, "Sex: %s\n", DG1->sex);
+ furi_string_cat_printf(
+ temp_str,
+ "Expiry Date: %02d %s %02d\n",
+ DG1->expiry_date.day,
+ months[DG1->expiry_date.month],
+ DG1->expiry_date.year);
+
+ widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
+ furi_string_free(temp_str);
+
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_auth_widget_callback, nfc);
+ /*
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_auth_widget_callback, nfc);
+ widget_add_button_element(
+ nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_auth_widget_callback, nfc);
+ */
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_passport_read_auth_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeLeft) {
+ nfc->dev->dev_data.mrtd_data.auth_success = false;
+ nfc->dev->dev_data.mrtd_data.auth.method = MrtdAuthMethodNone;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
+ consumed = true;
+ } else if(event.event == GuiButtonTypeCenter) {
+ //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth);
+ //consumed = true;
+ } else if(event.event == GuiButtonTypeRight) {
+ //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu);
+ //consumed = true;
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
+ consumed = true;
+ }
+ return consumed;
+}
+
+void nfc_scene_passport_read_auth_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clear view
+ widget_reset(nfc->widget);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c
index 13d5967a4..7b877d645 100644
--- a/applications/main/nfc/scenes/nfc_scene_read.c
+++ b/applications/main/nfc/scenes/nfc_scene_read.c
@@ -39,7 +39,6 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) {
void nfc_scene_read_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcRead);
nfc_device_clear(nfc->dev);
// Setup view
@@ -62,26 +61,47 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
(event.event == NfcWorkerEventReadUidNfcV)) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadUidNfcA) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadMfUltralight) {
notification_message(nfc->notifications, &sequence_success);
+ // Set unlock password input to 0xFFFFFFFF only on fresh read
+ memset(nfc->byte_input_store, 0xFF, 4);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadMfClassicDone) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadMfDesfire) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadBankCard) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess);
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
+ consumed = true;
+ } else if(event.event == NfcWorkerEventReadPassport) {
+ notification_message(nfc->notifications, &sequence_success);
+ FURI_LOG_D(
+ "NFC",
+ "Read passport, auth: %d, success: %d",
+ nfc->dev->dev_data.mrtd_data.auth.method,
+ nfc->dev->dev_data.mrtd_data.auth_success);
+ if(nfc->dev->dev_data.mrtd_data.auth_success) {
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadAuthSuccess);
+ } else {
+ scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess);
+ }
consumed = true;
} else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) {
diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_success.c b/applications/main/nfc/scenes/nfc_scene_read_card_success.c
index 352cb4a7e..9b2a2188e 100644
--- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_read_card_success_widget_callback(
GuiButtonType result,
@@ -18,7 +17,6 @@ void nfc_scene_read_card_success_on_enter(void* context) {
FuriString* temp_str;
temp_str = furi_string_alloc();
- DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup view
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c
new file mode 100644
index 000000000..94262aa1e
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c
@@ -0,0 +1,97 @@
+#include "../nfc_i.h"
+#include "nfc_worker_i.h"
+
+enum SubmenuIndex {
+ SubmenuIndexReadMifareClassic,
+ SubmenuIndexReadMifareDesfire,
+ SubmenuIndexReadMfUltralight,
+ SubmenuIndexReadEMV,
+ SubmenuIndexReadNFCA,
+};
+
+void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) {
+ Nfc* nfc = context;
+
+ view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_read_card_type_on_enter(void* context) {
+ Nfc* nfc = context;
+ Submenu* submenu = nfc->submenu;
+
+ submenu_add_item(
+ submenu,
+ "Read Mifare Classic",
+ SubmenuIndexReadMifareClassic,
+ nfc_scene_read_card_type_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Read Mifare DESFire",
+ SubmenuIndexReadMifareDesfire,
+ nfc_scene_read_card_type_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Read NTAG/Ultralight",
+ SubmenuIndexReadMfUltralight,
+ nfc_scene_read_card_type_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Read EMV card",
+ SubmenuIndexReadEMV,
+ nfc_scene_read_card_type_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Read NFC-A data",
+ SubmenuIndexReadNFCA,
+ nfc_scene_read_card_type_submenu_callback,
+ nfc);
+ uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType);
+ submenu_set_selected_item(submenu, state);
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
+}
+
+bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) {
+ Nfc* nfc = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubmenuIndexReadMifareClassic) {
+ nfc->dev->dev_data.read_mode = NfcReadModeMfClassic;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ consumed = true;
+ }
+ if(event.event == SubmenuIndexReadMifareDesfire) {
+ nfc->dev->dev_data.read_mode = NfcReadModeMfDesfire;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ consumed = true;
+ }
+ if(event.event == SubmenuIndexReadMfUltralight) {
+ nfc->dev->dev_data.read_mode = NfcReadModeMfUltralight;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ consumed = true;
+ }
+ if(event.event == SubmenuIndexReadEMV) {
+ nfc->dev->dev_data.read_mode = NfcReadModeEMV;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ consumed = true;
+ }
+ if(event.event == SubmenuIndexReadNFCA) {
+ nfc->dev->dev_data.read_mode = NfcReadModeNFCA;
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ consumed = true;
+ }
+ scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event);
+ }
+ return consumed;
+}
+
+void nfc_scene_read_card_type_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ submenu_reset(nfc->submenu);
+}
diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c
index e807e3d7f..43d476ea7 100644
--- a/applications/main/nfc/scenes/nfc_scene_save_name.c
+++ b/applications/main/nfc/scenes/nfc_scene_save_name.c
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
void nfc_scene_save_name_text_input_callback(void* context) {
Nfc* nfc = context;
@@ -61,8 +62,15 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
}
strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1);
- if(nfc_device_save(nfc->dev, nfc->text_store)) {
+ if(nfc_save_file(nfc)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
+ if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
+ // Nothing, do not count editing as saving
+ } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedNfcAddSave);
+ } else {
+ DOLPHIN_DEED(DolphinDeedNfcSave);
+ }
consumed = true;
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c
index dcd2519f1..bd7c9817e 100644
--- a/applications/main/nfc/scenes/nfc_scene_save_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_save_success.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_save_success_popup_callback(void* context) {
Nfc* nfc = context;
@@ -8,7 +7,6 @@ void nfc_scene_save_success_popup_callback(void* context) {
void nfc_scene_save_success_on_enter(void* context) {
Nfc* nfc = context;
- DOLPHIN_DEED(DolphinDeedNfcSave);
// Setup view
Popup* popup = nfc->popup;
@@ -33,6 +31,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneSavedMenu);
+ } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcScenePassportAuth)) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ nfc->scene_manager, NfcScenePassportAuth);
} else {
consumed = scene_manager_search_and_switch_to_another_scene(
nfc->scene_manager, NfcSceneFileSelect);
diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
index fe65b5b8a..a8a850ece 100644
--- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
@@ -1,12 +1,18 @@
#include "../nfc_i.h"
+#include
enum SubmenuIndex {
SubmenuIndexEmulate,
SubmenuIndexEditUid,
+ SubmenuIndexDetectReader,
+ SubmenuIndexWrite,
+ SubmenuIndexUpdate,
SubmenuIndexRename,
SubmenuIndexDelete,
SubmenuIndexInfo,
SubmenuIndexRestoreOriginal,
+ SubmenuIndexMfUlUnlockByReader,
+ SubmenuIndexMfUlUnlockByPassword,
};
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
@@ -20,7 +26,8 @@ void nfc_scene_saved_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu;
if(nfc->dev->format == NfcDeviceSaveFormatUid ||
- nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) {
+ nfc->dev->format == NfcDeviceSaveFormatMifareDesfire ||
+ nfc->dev->format == NfcDeviceSaveFormatBankCard) {
submenu_add_item(
submenu,
"Emulate UID",
@@ -41,8 +48,45 @@ void nfc_scene_saved_menu_on_enter(void* context) {
submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
}
+ if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
+ if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
+ submenu_add_item(
+ submenu,
+ "Detect reader",
+ SubmenuIndexDetectReader,
+ nfc_scene_saved_menu_submenu_callback,
+ nfc);
+ }
+ submenu_add_item(
+ submenu,
+ "Write To Initial Card",
+ SubmenuIndexWrite,
+ nfc_scene_saved_menu_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Update From Initial Card",
+ SubmenuIndexUpdate,
+ nfc_scene_saved_menu_submenu_callback,
+ nfc);
+ }
submenu_add_item(
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
+ if(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
+ !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
+ submenu_add_item(
+ submenu,
+ "Unlock With Reader",
+ SubmenuIndexMfUlUnlockByReader,
+ nfc_scene_saved_menu_submenu_callback,
+ nfc);
+ submenu_add_item(
+ submenu,
+ "Unlock With Password",
+ SubmenuIndexMfUlUnlockByPassword,
+ nfc_scene_saved_menu_submenu_callback,
+ nfc);
+ }
if(nfc->dev->shadow_file_exist) {
submenu_add_item(
submenu,
@@ -76,6 +120,16 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
}
+ DOLPHIN_DEED(DolphinDeedNfcEmulate);
+ consumed = true;
+ } else if(event.event == SubmenuIndexDetectReader) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
+ consumed = true;
+ } else if(event.event == SubmenuIndexWrite) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
+ consumed = true;
+ } else if(event.event == SubmenuIndexUpdate) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate);
consumed = true;
} else if(event.event == SubmenuIndexRename) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
@@ -105,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexRestoreOriginal) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
consumed = true;
+ } else if(event.event == SubmenuIndexMfUlUnlockByReader) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
+ consumed = true;
+ } else if(event.event == SubmenuIndexMfUlUnlockByPassword) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
+ consumed = true;
}
}
diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c
index 9622ba213..5f0f52f6e 100644
--- a/applications/main/nfc/scenes/nfc_scene_set_uid.c
+++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c
@@ -1,5 +1,4 @@
#include "../nfc_i.h"
-#include
void nfc_scene_set_uid_byte_input_callback(void* context) {
Nfc* nfc = context;
@@ -30,10 +29,9 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) {
- DOLPHIN_DEED(DolphinDeedNfcAddSave);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
- if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) {
+ if(nfc_save_file(nfc)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
consumed = true;
}
@@ -43,6 +41,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
}
}
}
+
return consumed;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c
index 1a9051dfd..f8b9f52c6 100644
--- a/applications/main/nfc/scenes/nfc_scene_start.c
+++ b/applications/main/nfc/scenes/nfc_scene_start.c
@@ -1,4 +1,6 @@
#include "../nfc_i.h"
+#include "nfc_worker_i.h"
+#include
enum SubmenuIndex {
SubmenuIndexRead,
@@ -46,12 +48,16 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
+ nfc->dev->dev_data.read_mode = NfcReadModeAuto;
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
+ DOLPHIN_DEED(DolphinDeedNfcRead);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
if(sd_exist) {
+ nfc_device_data_clear(&nfc->dev->dev_data);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
+ DOLPHIN_DEED(DolphinDeedNfcDetectReader);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
}
diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c
index 2dbb4338b..e5951beb2 100644
--- a/applications/main/nfc/views/detect_reader.c
+++ b/applications/main/nfc/views/detect_reader.c
@@ -1,7 +1,9 @@
#include "detect_reader.h"
-
+#include
#include
+#define DETECT_READER_UID_MAX_LEN (10)
+
struct DetectReader {
View* view;
DetectReaderDoneCallback callback;
@@ -12,6 +14,7 @@ typedef struct {
uint16_t nonces;
uint16_t nonces_max;
DetectReaderState state;
+ FuriString* uid_str;
} DetectReaderViewModel;
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
@@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) {
if(m->state == DetectReaderStateStart) {
snprintf(text, sizeof(text), "Touch the reader");
canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
+ if(furi_string_size(m->uid_str)) {
+ elements_multiline_text_aligned(
+ canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str));
+ }
} else if(m->state == DetectReaderStateReaderDetected) {
snprintf(text, sizeof(text), "Move the Flipper away");
canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);
@@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() {
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
view_set_context(detect_reader->view, detect_reader);
+ with_view_model(
+ detect_reader->view,
+ DetectReaderViewModel * model,
+ { model->uid_str = furi_string_alloc(); },
+ false);
+
return detect_reader;
}
void detect_reader_free(DetectReader* detect_reader) {
furi_assert(detect_reader);
+ with_view_model(
+ detect_reader->view,
+ DetectReaderViewModel * model,
+ { furi_string_free(model->uid_str); },
+ false);
+
view_free(detect_reader->view);
free(detect_reader);
}
@@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) {
model->nonces = 0;
model->nonces_max = 0;
model->state = DetectReaderStateStart;
+ furi_string_reset(model->uid_str);
},
false);
}
@@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat
with_view_model(
detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true);
}
+
+void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) {
+ furi_assert(detect_reader);
+ furi_assert(uid);
+ furi_assert(uid_len < DETECT_READER_UID_MAX_LEN);
+ with_view_model(
+ detect_reader->view,
+ DetectReaderViewModel * model,
+ {
+ furi_string_set_str(model->uid_str, "UID:");
+ for(size_t i = 0; i < uid_len; i++) {
+ furi_string_cat_printf(model->uid_str, " %02X", uid[i]);
+ }
+ },
+ true);
+}
diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h
index aabdd7c87..6481216b4 100644
--- a/applications/main/nfc/views/detect_reader.h
+++ b/applications/main/nfc/views/detect_reader.h
@@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m
void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);
void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);
+
+void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len);
diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c
index f821feaa9..dbf34c970 100644
--- a/applications/main/subghz/helpers/subghz_chat.c
+++ b/applications/main/subghz/helpers/subghz_chat.c
@@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
instance->cli = cli;
- instance->thread = furi_thread_alloc();
- furi_thread_set_name(instance->thread, "SubGhzChat");
- furi_thread_set_stack_size(instance->thread, 2048);
- furi_thread_set_context(instance->thread, instance);
- furi_thread_set_callback(instance->thread, subghz_chat_worker_thread);
+ instance->thread =
+ furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance);
instance->subghz_txrx = subghz_tx_rx_worker_alloc();
instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));
return instance;
diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
index 88aa4a18c..c4140b158 100644
--- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
+++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
@@ -72,6 +72,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
.frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0};
float rssi = 0;
uint32_t frequency = 0;
+ float rssi_temp = 0;
+ uint32_t frequency_temp = 0;
CC1101Status status;
//Start CC1101
@@ -195,6 +197,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine);
instance->sample_hold_counter = 20;
+ rssi_temp = frequency_rssi.rssi_fine;
+ frequency_temp = frequency_rssi.frequency_fine;
+
if(instance->filVal) {
frequency_rssi.frequency_fine =
subghz_frequency_analyzer_worker_expRunningAverageAdaptive(
@@ -203,7 +208,10 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// Deliver callback
if(instance->pair_callback) {
instance->pair_callback(
- instance->context, frequency_rssi.frequency_fine, frequency_rssi.rssi_fine);
+ instance->context,
+ frequency_rssi.frequency_fine,
+ frequency_rssi.rssi_fine,
+ true);
}
} else if( // Deliver results coarse
(frequency_rssi.rssi_coarse > instance->trigger_level) &&
@@ -215,6 +223,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
(double)frequency_rssi.rssi_coarse);
instance->sample_hold_counter = 20;
+ rssi_temp = frequency_rssi.rssi_coarse;
+ frequency_temp = frequency_rssi.frequency_coarse;
if(instance->filVal) {
frequency_rssi.frequency_coarse =
subghz_frequency_analyzer_worker_expRunningAverageAdaptive(
@@ -225,14 +235,22 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
instance->pair_callback(
instance->context,
frequency_rssi.frequency_coarse,
- frequency_rssi.rssi_coarse);
+ frequency_rssi.rssi_coarse,
+ true);
}
} else {
if(instance->sample_hold_counter > 0) {
instance->sample_hold_counter--;
+ if(instance->sample_hold_counter == 18) {
+ if(instance->pair_callback) {
+ instance->pair_callback(
+ instance->context, frequency_temp, rssi_temp, false);
+ }
+ }
} else {
instance->filVal = 0;
- if(instance->pair_callback) instance->pair_callback(instance->context, 0, 0);
+ if(instance->pair_callback)
+ instance->pair_callback(instance->context, 0, 0, false);
}
}
}
@@ -256,7 +274,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
SubGhz* subghz = context;
instance->setting = subghz->setting;
- instance->trigger_level = SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD;
+ instance->trigger_level = subghz->last_settings->frequency_analyzer_trigger;
+ //instance->trigger_level = SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD;
return instance;
}
diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h
index 3b93f60ad..eba4409ce 100644
--- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h
+++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h
@@ -5,8 +5,11 @@
typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;
-typedef void (
- *SubGhzFrequencyAnalyzerWorkerPairCallback)(void* context, uint32_t frequency, float rssi);
+typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)(
+ void* context,
+ uint32_t frequency,
+ float rssi,
+ bool signal);
typedef struct {
uint32_t frequency_coarse;
diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h
index 906879020..a981342b5 100644
--- a/applications/main/subghz/helpers/subghz_types.h
+++ b/applications/main/subghz/helpers/subghz_types.h
@@ -72,18 +72,7 @@ typedef enum {
SubGhzViewIdTestPacket,
} SubGhzViewId;
-struct SubGhzPresetDefinition {
- FuriString* name;
- uint32_t frequency;
- uint8_t* data;
- size_t data_size;
-};
-
-typedef struct SubGhzPresetDefinition SubGhzPresetDefinition;
-
typedef enum {
SubGhzViewReceiverModeLive,
SubGhzViewReceiverModeFile,
} SubGhzViewReceiverMode;
-
-#define SUBGHZ_HISTORY_REMOVE_SAVED_ITEMS 1
diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c
index 9a07be5f5..ca6c49b66 100644
--- a/applications/main/subghz/scenes/subghz_scene_delete_success.c
+++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c
@@ -44,14 +44,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
void subghz_scene_delete_success_on_exit(void* context) {
SubGhz* subghz = context;
-
- // Clear view
Popup* popup = subghz->popup;
- popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
- popup_set_callback(popup, NULL);
- popup_set_context(popup, NULL);
- popup_set_timeout(popup, 0);
- popup_disable_timeout(popup);
+
+ popup_reset(popup);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c
index 1a0992f94..bed044244 100644
--- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c
+++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c
@@ -1,8 +1,19 @@
#include "../subghz_i.h"
-#include
+#include "../views/subghz_frequency_analyzer.h"
#define TAG "SubGhzSceneFrequencyAnalyzer"
+static const NotificationSequence sequence_saved = {
+ &message_blink_stop,
+ &message_blue_0,
+ &message_green_255,
+ &message_red_0,
+ &message_vibro_on,
+ &message_delay_100,
+ &message_vibro_off,
+ NULL,
+};
+
void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
@@ -11,7 +22,6 @@ void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* con
void subghz_scene_frequency_analyzer_on_enter(void* context) {
SubGhz* subghz = context;
- DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer);
subghz_frequency_analyzer_set_callback(
subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz);
subghz_frequency_analyzer_feedback_level(
@@ -24,7 +34,28 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) {
bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubGhzCustomEventViewReceiverOK) {
+ if(event.event == SubGhzCustomEventSceneAnalyzerLock) {
+ notification_message(subghz->notifications, &sequence_set_green_255);
+ switch(subghz_frequency_analyzer_feedback_level(
+ subghz->subghz_frequency_analyzer,
+ SubGHzFrequencyAnalyzerFeedbackLevelAll,
+ false)) {
+ case SubGHzFrequencyAnalyzerFeedbackLevelAll:
+ notification_message(subghz->notifications, &sequence_success);
+ break;
+ case SubGHzFrequencyAnalyzerFeedbackLevelVibro:
+ notification_message(subghz->notifications, &sequence_single_vibro);
+ break;
+ case SubGHzFrequencyAnalyzerFeedbackLevelMute:
+ break;
+ }
+ notification_message(subghz->notifications, &sequence_display_backlight_on);
+ return true;
+ } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) {
+ notification_message(subghz->notifications, &sequence_reset_rgb);
+ return true;
+ } else if(event.event == SubGhzCustomEventViewReceiverOK) {
+ notification_message(subghz->notifications, &sequence_saved);
uint32_t frequency =
subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer);
if(frequency > 0) {
@@ -51,5 +82,7 @@ void subghz_scene_frequency_analyzer_on_exit(void* context) {
subghz->last_settings->frequency_analyzer_feedback_level =
subghz_frequency_analyzer_feedback_level(subghz->subghz_frequency_analyzer, 0, false);
+ subghz->last_settings->frequency_analyzer_trigger =
+ subghz_frequency_analyzer_get_trigger_level(subghz->subghz_frequency_analyzer);
subghz_last_settings_save(subghz->last_settings);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_more_raw.c b/applications/main/subghz/scenes/subghz_scene_more_raw.c
index f90d95de6..95d586934 100644
--- a/applications/main/subghz/scenes/subghz_scene_more_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_more_raw.c
@@ -46,23 +46,47 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexDelete) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW);
- return true;
+ if(subghz_file_available(subghz)) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW);
+ return true;
+ } else {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
+ }
} else if(event.event == SubmenuIndexEdit) {
- furi_string_reset(subghz->file_path_tmp);
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
- return true;
+ if(subghz_file_available(subghz)) {
+ furi_string_reset(subghz->file_path_tmp);
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
+ return true;
+ } else {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
+ }
} else if(event.event == SubmenuIndexDecode) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW);
- return true;
+ if(subghz_file_available(subghz)) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW);
+ return true;
+ } else {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
+ }
}
}
return false;
diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c
index 6f04be669..2cfe060c7 100644
--- a/applications/main/subghz/scenes/subghz_scene_need_saving.c
+++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c
@@ -48,12 +48,7 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubGhzCustomEventSceneExit) {
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
- subghz_preset_init(
- subghz,
- subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset),
- subghz->last_settings->frequency,
- NULL,
- 0);
+ subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0);
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
} else {
diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c
index 329d9b7e5..cfd371651 100644
--- a/applications/main/subghz/scenes/subghz_scene_read_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c
@@ -7,6 +7,7 @@
#define RAW_FILE_NAME "R_"
#define TAG "SubGhzSceneReadRAW"
+#define RAW_THRESHOLD_RSSI_LOW_COUNT 10
bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
bool ret = false;
@@ -78,24 +79,33 @@ void subghz_scene_read_raw_on_enter(void* context) {
switch(subghz->txrx->rx_key_state) {
case SubGhzRxKeyStateBack:
- subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "");
+ subghz_read_raw_set_status(
+ subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi);
break;
case SubGhzRxKeyStateRAWLoad:
path_extract_filename(subghz->file_path, file_name, true);
subghz_read_raw_set_status(
subghz->subghz_read_raw,
SubGhzReadRAWStatusLoadKeyTX,
- furi_string_get_cstr(file_name));
+ furi_string_get_cstr(file_name),
+ subghz->txrx->raw_threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break;
case SubGhzRxKeyStateRAWSave:
path_extract_filename(subghz->file_path, file_name, true);
subghz_read_raw_set_status(
- subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, furi_string_get_cstr(file_name));
+ subghz->subghz_read_raw,
+ SubGhzReadRAWStatusSaveKey,
+ furi_string_get_cstr(file_name),
+ subghz->txrx->raw_threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break;
default:
- subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "");
+ subghz_read_raw_set_status(
+ subghz->subghz_read_raw,
+ SubGhzReadRAWStatusStart,
+ "",
+ subghz->txrx->raw_threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break;
}
@@ -152,13 +162,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
NULL,
0);
} else {
- subghz_preset_init(
- subghz,
- subghz_setting_get_preset_name(
- subghz->setting, subghz->last_settings->preset),
- subghz->last_settings->frequency,
- NULL,
- 0);
+ subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0);
}
if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) {
@@ -207,20 +211,28 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break;
case SubGhzCustomEventViewReadRAWMore:
- if(subghz_scene_read_raw_update_filename(subghz)) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
- subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
- consumed = true;
+ if(subghz_file_available(subghz)) {
+ if(subghz_scene_read_raw_update_filename(subghz)) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
+ subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
+ consumed = true;
+ } else {
+ furi_crash("SubGhz: RAW file name update error.");
+ }
} else {
- furi_crash("SubGhz: RAW file name update error.");
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
}
break;
case SubGhzCustomEventViewReadRAWSendStart:
- if(subghz_scene_read_raw_update_filename(subghz)) {
+ if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {
//start send
subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
@@ -232,7 +244,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
} else {
- DOLPHIN_DEED(DolphinDeedSubGhzSend);
+ 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(
@@ -242,6 +259,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
subghz->state_notifications = SubGhzNotificationStateTx;
}
}
+ } else {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
}
consumed = true;
break;
@@ -331,6 +354,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
__LL_RTC_CONVERT_BCD2BIN((time >> 8) & 0xFF) // DAY
);
//subghz_get_preset_name(subghz, subghz->error_str);
+ 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,
strings[0],
@@ -356,11 +380,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break;
case SubGhzCustomEventViewReadRAWSave:
- if(subghz_scene_read_raw_update_filename(subghz)) {
+ 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;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
+ } else {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart)) {
+ scene_manager_stop(subghz->scene_manager);
+ view_dispatcher_stop(subghz->view_dispatcher);
+ }
}
consumed = true;
break;
@@ -376,7 +406,35 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
subghz->subghz_read_raw,
subghz_protocol_raw_get_sample_write(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result));
- subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
+
+ float rssi = furi_hal_subghz_get_rssi();
+
+ if(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);
+ } 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);
+ }
+ }
+
break;
case SubGhzNotificationStateTx:
notification_message(subghz->notifications, &sequence_blink_magenta_10);
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c
index 87a7c9552..cab19f730 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver.c
@@ -1,5 +1,6 @@
#include "../subghz_i.h"
#include "../views/receiver.h"
+#include
#define TAG "SubGhzSceneReceiver"
@@ -116,12 +117,7 @@ void subghz_scene_receiver_on_enter(void* context) {
str_buff = furi_string_alloc();
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) {
- subghz_preset_init(
- subghz,
- subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset),
- subghz->last_settings->frequency,
- NULL,
- 0);
+ subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0);
subghz_history_reset(subghz->txrx->history);
subghz->txrx->rx_key_state = SubGhzRxKeyStateStart;
}
@@ -186,12 +182,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
- subghz_preset_init(
- subghz,
- subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset),
- subghz->last_settings->frequency,
- NULL,
- 0);
+ subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0);
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
}
@@ -202,6 +193,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz->txrx->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:
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
index f3664a46a..294cc1bc0 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
@@ -1,4 +1,5 @@
#include "../subghz_i.h"
+#include
#include
@@ -11,6 +12,36 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexDetectRaw,
SubGhzSettingIndexRSSIThreshold,
SubGhzSettingIndexLock,
+ SubGhzSettingIndexRAWThesholdRSSI,
+};
+
+#define RAW_THRESHOLD_RSSI_COUNT 11
+const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
+ "-----",
+ "-85.0",
+ "-80.0",
+ "-75.0",
+ "-70.0",
+ "-65.0",
+ "-60.0",
+ "-55.0",
+ "-50.0",
+ "-45.0",
+ "-40.0",
+
+};
+const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
+ -90.0f,
+ -85.0f,
+ -80.0f,
+ -75.0f,
+ -70.0f,
+ -65.0f,
+ -60.0f,
+ -55.0f,
+ -50.0f,
+ -45.0f,
+ -40.0f,
};
#define HOPPING_COUNT 2
@@ -164,7 +195,7 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
const char* preset_name = subghz_setting_get_preset_name(subghz->setting, index);
variable_item_set_current_value_text(item, preset_name);
- subghz->last_settings->preset = index;
+ //subghz->last_settings->preset = index;
subghz_preset_init(
subghz,
@@ -249,6 +280,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
}*/
}
+static void subghz_scene_receiver_config_set_raw_threshold_rssi(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, raw_theshold_rssi_text[index]);
+ subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index];
+}
+
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhz* subghz = context;
@@ -356,7 +395,19 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_var_list_enter_callback,
subghz);
}
-
+ if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
+ SubGhzCustomEventManagerSet) {
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "RSSI Threshold:",
+ RAW_THRESHOLD_RSSI_COUNT,
+ 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);
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);
+ }
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
index 3713b85c0..e9c849e1e 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
@@ -1,6 +1,5 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-#include
#include
#include
@@ -31,8 +30,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
subghz->txrx->decoder_result,
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
- SubGhzPresetDefinition* preset =
- subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
+ SubGhzRadioPreset* preset =
+ subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
subghz_preset_init(
subghz,
furi_string_get_cstr(preset->name),
@@ -115,7 +114,6 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
void subghz_scene_receiver_info_on_enter(void* context) {
SubGhz* subghz = context;
- DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
subghz_scene_receiver_info_draw_widget(subghz);
}
@@ -172,9 +170,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
//CC1101 Stop RX -> Save
subghz->state_notifications = SubGhzNotificationStateIDLE;
- if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
- subghz->txrx->hopper_state = SubGhzHopperStateOFF;
- }
+ subghz->txrx->hopper_state = SubGhzHopperStateOFF;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);
diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c
index 4ccc15f9e..0422a37a4 100644
--- a/applications/main/subghz/scenes/subghz_scene_rpc.c
+++ b/applications/main/subghz/scenes/subghz_scene_rpc.c
@@ -42,9 +42,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
bool result = false;
if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) &&
(state == SubGhzRpcStateLoaded)) {
- subghz_blink_start(subghz);
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
- result = true;
+ if(result) subghz_blink_start(subghz);
}
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c
index 71d16bc65..d0b2af35e 100644
--- a/applications/main/subghz/scenes/subghz_scene_save_name.c
+++ b/applications/main/subghz/scenes/subghz_scene_save_name.c
@@ -4,6 +4,7 @@
#include "../helpers/subghz_custom_event.h"
#include
#include
+#include
#define MAX_TEXT_INPUT_LEN 23
@@ -132,6 +133,17 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
}
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
+ if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSavedMenu)) {
+ // Nothing, do not count editing as saving
+ } else if(scene_manager_has_previous_scene(
+ subghz->scene_manager, SubGhzSceneMoreRAW)) {
+ // Ditto, for RAW signals
+ } else if(scene_manager_has_previous_scene(
+ subghz->scene_manager, SubGhzSceneSetType)) {
+ DOLPHIN_DEED(DolphinDeedSubGhzAddManually);
+ } else {
+ DOLPHIN_DEED(DolphinDeedSubGhzSave);
+ }
return true;
} else {
furi_string_set(subghz->error_str, "No name file");
diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c
index 62efa5e41..85be80b2a 100644
--- a/applications/main/subghz/scenes/subghz_scene_save_success.c
+++ b/applications/main/subghz/scenes/subghz_scene_save_success.c
@@ -1,7 +1,5 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-#include
-#include
void subghz_scene_save_success_popup_callback(void* context) {
SubGhz* subghz = context;
@@ -10,7 +8,6 @@ void subghz_scene_save_success_popup_callback(void* context) {
void subghz_scene_save_success_on_enter(void* context) {
SubGhz* subghz = context;
- DOLPHIN_DEED(DolphinDeedSubGhzSave);
// Setup view
Popup* popup = subghz->popup;
@@ -74,11 +71,6 @@ void subghz_scene_save_success_on_exit(void* context) {
// Clear view
Popup* popup = subghz->popup;
- popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
- popup_set_callback(popup, NULL);
- popup_set_context(popup, NULL);
- popup_set_timeout(popup, 0);
- popup_disable_timeout(popup);
+
+ popup_reset(popup);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
index dcb7e6850..1d6f9d70b 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
@@ -67,6 +67,8 @@ bool subghz_scene_set_seed_bft_on_event(void* context, SceneManagerEvent event)
flipper_format_write_hex(
subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t));
+ flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT");
+
generated_protocol = true;
} else {
generated_protocol = false;
diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c
index dd36ad08c..3ee823997 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_type.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_type.c
@@ -4,10 +4,9 @@
#include
#include
#include
-#include
#include
#include
-#include
+#include
#define TAG "SubGhzSetType"
@@ -466,7 +465,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
if(generated_protocol) {
subghz_file_name_clear(subghz);
- DOLPHIN_DEED(DolphinDeedSubGhzAddManually);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
return true;
}
diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
index 2720b2b94..113e7ae74 100644
--- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
+++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
@@ -36,16 +36,10 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event
void subghz_scene_show_error_sub_on_exit(void* context) {
SubGhz* subghz = context;
-
- // Clear view
Popup* popup = subghz->popup;
- popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
- popup_set_callback(popup, NULL);
- popup_set_context(popup, NULL);
- popup_set_timeout(popup, 0);
- popup_disable_timeout(popup);
+
+ popup_reset(popup);
+
furi_string_reset(subghz->error_str);
notification_message(subghz->notifications, &sequence_reset_rgb);
diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c
index 18608dc5b..febbea5fa 100644
--- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c
+++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c
@@ -39,14 +39,7 @@ bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event)
void subghz_scene_show_only_rx_on_exit(void* context) {
SubGhz* subghz = context;
-
- // Clear view
Popup* popup = subghz->popup;
- popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
- popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
- popup_set_icon(popup, 0, 0, NULL);
- popup_set_callback(popup, NULL);
- popup_set_context(popup, NULL);
- popup_set_timeout(popup, 0);
- popup_disable_timeout(popup);
+
+ popup_reset(popup);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c
index cf8cd2499..03e2f9b06 100644
--- a/applications/main/subghz/scenes/subghz_scene_start.c
+++ b/applications/main/subghz/scenes/subghz_scene_start.c
@@ -1,4 +1,5 @@
#include "../subghz_i.h"
+#include
#include
@@ -96,6 +97,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
+ DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer);
return true;
} else if(event.event == SubmenuIndexTest) {
diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c
index 2d172de67..dff4f8384 100644
--- a/applications/main/subghz/scenes/subghz_scene_transmitter.c
+++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c
@@ -52,7 +52,6 @@ bool subghz_scene_transmitter_update_data_show(void* context) {
void subghz_scene_transmitter_on_enter(void* context) {
SubGhz* subghz = context;
- DOLPHIN_DEED(DolphinDeedSubGhzSend);
if(!subghz_scene_transmitter_update_data_show(subghz)) {
view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError);
@@ -80,6 +79,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
} else {
subghz->state_notifications = SubGhzNotificationStateTx;
subghz_scene_transmitter_update_data_show(subghz);
+ DOLPHIN_DEED(DolphinDeedSubGhzSend);
}
}
return true;
diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c
index 602268024..24a744199 100644
--- a/applications/main/subghz/subghz.c
+++ b/applications/main/subghz/subghz.c
@@ -3,6 +3,7 @@
#include
#include
#include "subghz_i.h"
+#include
#define TAG "SubGhzApp"
@@ -190,8 +191,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
// Load last used values for Read, Read RAW, etc. or default
if(!alloc_for_tx_only) {
subghz->last_settings = subghz_last_settings_alloc();
- subghz_last_settings_load(
- subghz->last_settings, subghz_setting_get_preset_count(subghz->setting));
+ subghz_last_settings_load(subghz->last_settings, 0);
#if FURI_DEBUG
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
FURI_LOG_D(
@@ -213,15 +213,10 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
//init Worker & Protocol & History & KeyBoard
subghz->lock = SubGhzLockOff;
subghz->txrx = malloc(sizeof(SubGhzTxRx));
- subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition));
+ subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
subghz->txrx->preset->name = furi_string_alloc();
if(!alloc_for_tx_only) {
- subghz_preset_init(
- subghz,
- subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset),
- subghz->last_settings->frequency,
- NULL,
- 0);
+ subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0);
} else {
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
@@ -233,6 +228,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->txrx->history = subghz_history_alloc();
}
+ subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->fff_data = flipper_format_string_alloc();
@@ -243,7 +239,8 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
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);
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
subghz_last_settings_set_detect_raw_values(subghz);
diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c
index e378eaccd..b93dd9d7c 100644
--- a/applications/main/subghz/subghz_cli.c
+++ b/applications/main/subghz/subghz_cli.c
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include "helpers/subghz_chat.h"
@@ -159,6 +160,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
SubGhzEnvironment* environment = subghz_environment_alloc();
+ subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
subghz_transmitter_deserialize(transmitter, flipper_format);
@@ -252,6 +254,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s"));
+ subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@@ -292,7 +295,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
furi_hal_power_suppress_charge_exit();
- printf("\r\nPackets recieved %u\r\n", instance->packet_count);
+ printf("\r\nPackets received %u\r\n", instance->packet_count);
// Cleanup
subghz_receiver_free(receiver);
@@ -371,6 +374,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s"));
+ subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@@ -399,7 +403,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
}
}
- printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count);
+ printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count);
// Cleanup
subghz_receiver_free(receiver);
@@ -429,7 +433,7 @@ static void subghz_cli_command_print_usage() {
printf("\r\n");
printf(" debug cmd:\r\n");
printf("\ttx_carrier \t - Transmit carrier\r\n");
- printf("\trx_carrier \t - Receiv carrier\r\n");
+ printf("\trx_carrier \t - Receive carrier\r\n");
printf(
"\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n");
printf(
diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c
index a5845d543..e8d3acfd7 100644
--- a/applications/main/subghz/subghz_history.c
+++ b/applications/main/subghz/subghz_history.c
@@ -29,7 +29,7 @@ typedef struct {
FuriString* protocol_name;
bool is_file;
uint8_t type;
- SubGhzPresetDefinition* preset;
+ SubGhzRadioPreset* preset;
} SubGhzHistoryItem;
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
@@ -63,9 +63,9 @@ FuriString* subghz_history_generate_temp_filename(uint32_t index) {
bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance) {
FileInfo file_info;
- storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info);
+ FS_Error error = storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info);
- if(storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info) == FSE_OK) {
+ if(error == FSE_OK) {
if(file_info.flags & FSF_DIRECTORY) {
return true;
}
@@ -123,7 +123,7 @@ void subghz_history_clear_tmp_dir(SubGhzHistory* instance) {
}
// Stage 2 - create dir if necessary
- res = !storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR);
+ res = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR);
if(!res) {
FURI_LOG_E(TAG, "Cannot process temp dir!");
}
@@ -194,7 +194,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) {
return item->preset->frequency;
}
-SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) {
+SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->preset;
@@ -321,7 +321,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
bool subghz_history_add_to_history(
SubGhzHistory* instance,
void* context,
- SubGhzPresetDefinition* preset) {
+ SubGhzRadioPreset* preset) {
furi_assert(instance);
furi_assert(context);
@@ -342,7 +342,7 @@ bool subghz_history_add_to_history(
FuriString* text;
text = furi_string_alloc();
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
- item->preset = malloc(sizeof(SubGhzPresetDefinition));
+ item->preset = malloc(sizeof(SubGhzRadioPreset));
item->type = decoder_base->protocol->type;
item->preset->frequency = preset->frequency;
item->preset->name = furi_string_alloc();
diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h
index f14253896..ee1ee1a4d 100644
--- a/applications/main/subghz/subghz_history.h
+++ b/applications/main/subghz/subghz_history.h
@@ -5,7 +5,7 @@
#include
#include
#include
-#include "helpers/subghz_types.h"
+#include
typedef struct SubGhzHistory SubGhzHistory;
@@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance);
*/
uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx);
-SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx);
+SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx);
/** Get preset to history[idx]
*
@@ -95,13 +95,13 @@ uint16_t subghz_history_get_last_index(SubGhzHistory* instance);
*
* @param instance - SubGhzHistory instance
* @param context - SubGhzProtocolCommon context
- * @param preset - SubGhzPresetDefinition preset
+ * @param preset - SubGhzRadioPreset preset
* @return bool;
*/
bool subghz_history_add_to_history(
SubGhzHistory* instance,
void* context,
- SubGhzPresetDefinition* preset);
+ SubGhzRadioPreset* preset);
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
*
diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c
index e3d22ed73..9660fa1aa 100644
--- a/applications/main/subghz/subghz_i.c
+++ b/applications/main/subghz/subghz_i.c
@@ -102,8 +102,8 @@ static bool subghz_tx(SubGhz* subghz, uint32_t 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);
- furi_hal_gpio_write(&gpio_cc1101_g0, true);
bool ret = furi_hal_subghz_tx();
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
return ret;
@@ -497,6 +497,23 @@ bool subghz_rename_file(SubGhz* subghz) {
return ret;
}
+bool subghz_file_available(SubGhz* subghz) {
+ furi_assert(subghz);
+ bool ret = true;
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ FS_Error fs_result =
+ storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL);
+
+ if(fs_result != FSE_OK) {
+ dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory");
+ ret = false;
+ }
+
+ furi_record_close(RECORD_STORAGE);
+ return ret;
+}
+
bool subghz_delete_file(SubGhz* subghz) {
furi_assert(subghz);
@@ -520,12 +537,6 @@ bool subghz_path_is_file(FuriString* path) {
}
uint32_t subghz_random_serial(void) {
- static bool rand_generator_inited = false;
-
- if(!rand_generator_inited) {
- srand(DWT->CYCCNT);
- rand_generator_inited = true;
- }
return (uint32_t)rand();
}
diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h
index e7b03e94f..dd511105e 100644
--- a/applications/main/subghz/subghz_i.h
+++ b/applications/main/subghz/subghz_i.h
@@ -1,6 +1,7 @@
#pragma once
#include "helpers/subghz_types.h"
+#include
#include "subghz.h"
#include "views/receiver.h"
#include "views/transmitter.h"
@@ -12,9 +13,8 @@
#include "views/subghz_test_static.h"
#include "views/subghz_test_packet.h"
#endif
-// #include
-// #include
#include
+#include
#include
#include
#include
@@ -26,16 +26,13 @@
#include
#include
-
#include
-
#include
-
+#include
#include
#include
#include "subghz_history.h"
-#include "subghz_setting.h"
#include "subghz_last_settings.h"
#include
@@ -68,7 +65,7 @@ struct SubGhzTxRx {
FlipperFormat* fff_data;
SecureData* secure_data;
- SubGhzPresetDefinition* preset;
+ SubGhzRadioPreset* preset;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state;
@@ -76,6 +73,9 @@ struct SubGhzTxRx {
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;
@@ -153,6 +153,7 @@ bool subghz_save_protocol_to_file(
const char* dev_file_name);
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);
diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c
index bf3638908..478e32347 100644
--- a/applications/main/subghz/subghz_last_settings.c
+++ b/applications/main/subghz/subghz_last_settings.c
@@ -14,6 +14,7 @@
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
+#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
#define SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW 0
@@ -21,8 +22,9 @@
#endif
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency"
-#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset"
+//#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset"
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel"
+#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger"
SubGhzLastSettings* subghz_last_settings_alloc(void) {
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
@@ -35,6 +37,7 @@ void subghz_last_settings_free(SubGhzLastSettings* instance) {
}
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count) {
+ UNUSED(preset_count);
furi_assert(instance);
#ifdef FURI_DEBUG
FURI_LOG_I(TAG, "subghz_last_settings_load");
@@ -45,16 +48,19 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
uint32_t temp_frequency = 0;
uint32_t temp_frequency_analyzer_feedback_level = 0;
- int32_t temp_preset = 0;
+ float temp_frequency_analyzer_trigger = 0;
+ //int32_t temp_preset = 0;
bool frequency_analyzer_feedback_level_was_read = false;
+ bool frequency_analyzer_trigger_was_read = false;
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
uint32_t temp_read_raw = 0;
#endif
if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH &&
flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) {
+ /*
flipper_format_read_int32(
- fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (int32_t*)&temp_preset, 1);
+ fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (int32_t*)&temp_preset, 1);*/
flipper_format_read_uint32(
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, (uint32_t*)&temp_frequency, 1);
frequency_analyzer_feedback_level_was_read = flipper_format_read_uint32(
@@ -62,6 +68,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL,
(uint32_t*)&temp_frequency_analyzer_feedback_level,
1);
+ frequency_analyzer_trigger_was_read = flipper_format_read_float(
+ fff_data_file,
+ SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
+ (float*)&temp_frequency_analyzer_trigger,
+ 1);
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
flipper_format_read_uint32(
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, (uint32_t*)&temp_read_raw, 1);
@@ -76,6 +87,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
instance->frequency_analyzer_feedback_level =
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
+ instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
instance->detect_raw = SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW;
#endif
@@ -85,16 +97,20 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
frequency_analyzer_feedback_level_was_read ?
temp_frequency_analyzer_feedback_level :
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
+
+ instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ?
+ temp_frequency_analyzer_trigger :
+ SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
instance->detect_raw = temp_read_raw;
#endif
- if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) {
- FURI_LOG_W(TAG, "Last used preset no found");
- instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
- } else {
+ /*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) {
+ FURI_LOG_W(TAG, "Last used preset no found");*/
+ instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
+ /*/} else {
instance->preset = temp_preset;
- }
+ }*/
}
flipper_format_file_close(fff_data_file);
@@ -125,10 +141,11 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
file, SUBGHZ_LAST_SETTING_FILE_TYPE, SUBGHZ_LAST_SETTING_FILE_VERSION))
break;
+ /*
if(!flipper_format_insert_or_update_int32(
file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset, 1)) {
break;
- }
+ }*/
if(!flipper_format_insert_or_update_uint32(
file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, &instance->frequency, 1)) {
break;
@@ -140,6 +157,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
1)) {
break;
}
+ if(!flipper_format_insert_or_update_float(
+ file,
+ SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
+ &instance->frequency_analyzer_trigger,
+ 1)) {
+ break;
+ }
#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
if(!flipper_format_insert_or_update_uint32(
file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, &instance->detect_raw, 1)) {
diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h
index cb6a058fe..ef3674d69 100644
--- a/applications/main/subghz/subghz_last_settings.h
+++ b/applications/main/subghz/subghz_last_settings.h
@@ -20,6 +20,7 @@ typedef struct {
#endif
int32_t preset;
uint32_t frequency_analyzer_feedback_level;
+ float frequency_analyzer_trigger;
} SubGhzLastSettings;
SubGhzLastSettings* subghz_last_settings_alloc(void);
diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c
index 30372a51d..ebbcc2453 100644
--- a/applications/main/subghz/views/receiver.c
+++ b/applications/main/subghz/views/receiver.c
@@ -30,9 +30,9 @@ typedef struct SubGhzReceiverHistory SubGhzReceiverHistory;
static const Icon* ReceiverItemIcons[] = {
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
- [SubGhzProtocolTypeStatic] = &I_Unlock_7x8,
- [SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
- [SubGhzProtocolTypeRAW] = &I_Unlock_7x8,
+ [SubGhzProtocolTypeStatic] = &I_Static_9x7,
+ [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7,
+ [SubGhzProtocolTypeRAW] = &I_Raw_9x7,
};
typedef enum {
@@ -217,7 +217,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
} else {
canvas_set_color(canvas, ColorBlack);
}
- canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
+ canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
furi_string_reset(str_buff);
}
diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c
index aed21b698..711ee5437 100644
--- a/applications/main/subghz/views/subghz_frequency_analyzer.c
+++ b/applications/main/subghz/views/subghz_frequency_analyzer.c
@@ -15,65 +15,17 @@
#define RSSI_MIN -97
#define RSSI_MAX -60
-#define RSSI_SCALE 2
+#define RSSI_SCALE 2.3
#define TRIGGER_STEP 1
-
-static const NotificationSequence sequence_hw_blink = {
- &message_blink_start_10,
- &message_blink_set_color_cyan,
- &message_do_not_reset,
- NULL,
-};
-
-static const NotificationSequence sequence_hw_blink_stop = {
- &message_blink_stop,
- NULL,
-};
-
-static const NotificationSequence sequence_saved = {
- &message_blink_stop,
- &message_blue_0,
- &message_green_255,
- &message_red_0,
- &message_vibro_on,
- &message_delay_100,
- &message_vibro_off,
- NULL,
-};
-
-static const NotificationSequence sequence_frequency = {
- &message_display_backlight_on,
- &message_green_255,
- &message_vibro_on,
- &message_delay_100,
- &message_green_0,
- &message_blue_255,
- &message_vibro_off,
- &message_delay_100,
- &message_blue_0,
- &message_green_255,
- &message_vibro_on,
- &message_delay_100,
- &message_green_0,
- &message_vibro_off,
- NULL,
-};
-
-//static const NotificationSequence sequence_not_saved = {
-// &message_blink_stop,
-// &message_green_255,
-// &message_blue_255,
-// &message_red_255,
-// NULL,
-//};
+#define MAX_HISTORY 4
static const uint32_t subghz_frequency_list[] = {
- 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000,
- 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000,
- 314350000, 315000000, 318000000, 345000000, 348000000, 387000000, 390000000,
- 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000,
- 434176948, 434420000, 434775000, 438900000, 464000000, 779000000, 868350000,
- 868400000, 868950000, 906400000, 915000000, 925000000, 928000000};
+ 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000,
+ 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000,
+ 315000000, 318000000, 330000000, 345000000, 348000000, 387000000, 390000000, 418000000,
+ 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, 434176948,
+ 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, 868350000,
+ 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000};
typedef enum {
SubGhzFrequencyAnalyzerStatusIDLE,
@@ -85,21 +37,27 @@ struct SubGhzFrequencyAnalyzer {
SubGhzFrequencyAnalyzerCallback callback;
void* context;
bool locked;
- uint8_t feedback_level; // 0 - no feedback, 1 - vibro only, 2 - vibro and sound
+ SubGHzFrequencyAnalyzerFeedbackLevel
+ feedback_level; // 0 - no feedback, 1 - vibro only, 2 - vibro and sound
float rssi_last;
- uint32_t frequency_last;
- uint32_t frequency_last_vis;
- NotificationApp* notifications;
+ uint8_t selected_index;
+ uint8_t max_index;
+ bool show_frame;
};
typedef struct {
uint32_t frequency;
- uint32_t frequency_last;
uint32_t frequency_to_save;
float rssi;
+ uint32_t history_frequency[MAX_HISTORY];
+ uint8_t history_frequency_rx_count[MAX_HISTORY];
+ bool signal;
float rssi_last;
float trigger;
- uint8_t feedback_level;
+ SubGHzFrequencyAnalyzerFeedbackLevel feedback_level;
+ uint8_t selected_index;
+ uint8_t max_index;
+ bool show_frame;
} SubGhzFrequencyAnalyzerModel;
void subghz_frequency_analyzer_set_callback(
@@ -121,23 +79,27 @@ void subghz_frequency_analyzer_draw_rssi(
uint8_t y) {
// Current RSSI
if(rssi) {
- if(rssi > RSSI_MAX) rssi = RSSI_MAX;
+ if(rssi > RSSI_MAX) {
+ rssi = RSSI_MAX;
+ }
rssi = (rssi - RSSI_MIN) / RSSI_SCALE;
uint8_t column_number = 0;
for(size_t i = 0; i <= (uint8_t)rssi; i++) {
if((i + 1) % 4) {
column_number++;
- canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number);
+ canvas_draw_box(canvas, x + 2 * i, (y + 4) - column_number, 2, column_number);
}
}
}
// Last RSSI
if(rssi_last) {
- if(rssi_last > RSSI_MAX) rssi_last = RSSI_MAX;
+ if(rssi_last > RSSI_MAX) {
+ rssi_last = RSSI_MAX;
+ }
int max_x = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) * 2;
//if(!(max_x % 8)) max_x -= 2;
- int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) + 4;
+ int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) + 1;
max_h -= (max_h / 4) + 3;
canvas_draw_line(canvas, x + max_x + 1, y - max_h, x + max_x + 1, y + 3);
}
@@ -151,18 +113,66 @@ void subghz_frequency_analyzer_draw_rssi(
canvas_draw_line(canvas, x, y + 3, x + (RSSI_MAX - RSSI_MIN) * 2 / RSSI_SCALE, y + 3);
}
+static void subghz_frequency_analyzer_history_frequency_draw(
+ Canvas* canvas,
+ SubGhzFrequencyAnalyzerModel* model) {
+ char buffer[64];
+ const uint8_t x1 = 2;
+ const uint8_t x2 = 66;
+ const uint8_t y = 37;
+
+ canvas_set_font(canvas, FontSecondary);
+ uint8_t line = 0;
+ bool show_frame = model->show_frame && model->max_index > 0;
+ for(uint8_t i = 0; i < MAX_HISTORY; i++) {
+ uint8_t current_x;
+ uint8_t current_y = y + line * 11;
+
+ if(i % 2 == 0) {
+ current_x = x1;
+ } else {
+ current_x = x2;
+ line++;
+ }
+ if(model->history_frequency[i]) {
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "%03ld.%03ld",
+ model->history_frequency[i] / 1000000 % 1000,
+ model->history_frequency[i] / 1000 % 1000);
+ canvas_draw_str(canvas, current_x, current_y, buffer);
+ } else {
+ canvas_draw_str(canvas, current_x, current_y, "---.---");
+ }
+ if(model->history_frequency_rx_count[i] > 0) {
+ snprintf(buffer, sizeof(buffer), "x%d", model->history_frequency_rx_count[i]);
+ canvas_draw_str(canvas, current_x + 41, current_y, buffer);
+ } else {
+ canvas_draw_str(canvas, current_x + 41, current_y, "MHz");
+ }
+
+ if(show_frame && i == model->selected_index) {
+ elements_frame(canvas, current_x - 2, current_y - 9, 63, 11);
+ }
+ }
+}
+
void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) {
char buffer[64];
// Title
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
+ canvas_draw_str(canvas, 20, 7, "Frequency Analyzer");
// RSSI
canvas_draw_str(canvas, 33, 62, "RSSI");
subghz_frequency_analyzer_draw_rssi(
- canvas, model->rssi, model->rssi_last, model->trigger, 57, 58);
+ canvas, model->rssi, model->rssi_last, model->trigger, 56, 57);
+
+ // Last detected frequency
+ subghz_frequency_analyzer_history_frequency_draw(canvas, model);
// Frequency
canvas_set_font(canvas, FontBigNumbers);
@@ -172,41 +182,40 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
"%03ld.%03ld",
model->frequency / 1000000 % 1000,
model->frequency / 1000 % 1000);
- canvas_draw_str(canvas, 8, 30, buffer);
- canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11);
-
- // Last detected frequency
- canvas_set_font(canvas, FontSecondary);
- if(model->frequency_last) {
- snprintf(
- buffer,
- sizeof(buffer),
- "Last: %03ld.%03ld MHz",
- model->frequency_last / 1000000 % 1000,
- model->frequency_last / 1000 % 1000);
- } else {
- strcpy(buffer, "Last: ---.--- MHz");
- }
- canvas_draw_str(canvas, 9, 42, buffer);
-
- switch(model->feedback_level) {
- case 2:
- canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Volup_8x6);
- break;
- case 1:
- canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Voldwn_6x6);
- break;
- case 0:
- canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Voldwn_6x6);
+ if(model->signal) {
+ canvas_draw_box(canvas, 4, 10, 121, 19);
canvas_set_color(canvas, ColorWhite);
- canvas_draw_box(canvas, 128 - 2 - 1 - 2, 1, 2, 6);
+ } else {
+ // TODO: Disable this
+ //canvas_draw_box(canvas, 4, 11, 121, 19);
+ //canvas_set_color(canvas, ColorWhite);
+ }
+
+ canvas_draw_str(canvas, 8, 26, buffer);
+ canvas_draw_icon(canvas, 96, 15, &I_MHz_25x11);
+
+ canvas_set_color(canvas, ColorBlack);
+ canvas_set_font(canvas, FontSecondary);
+ const uint8_t icon_x = 119;
+ switch(model->feedback_level) {
+ case SubGHzFrequencyAnalyzerFeedbackLevelAll:
+ canvas_draw_icon(canvas, icon_x, 1, &I_Volup_8x6);
+ break;
+ case SubGHzFrequencyAnalyzerFeedbackLevelVibro:
+ canvas_draw_icon(canvas, icon_x, 1, &I_Voldwn_6x6);
+ break;
+ case SubGHzFrequencyAnalyzerFeedbackLevelMute:
+ canvas_draw_icon(canvas, icon_x, 1, &I_Voldwn_6x6);
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_box(canvas, 123, 1, 2, 6);
canvas_set_color(canvas, ColorBlack);
break;
}
// Buttons hint
+ canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "T-");
- elements_button_right(canvas, "T+");
+ elements_button_right(canvas, "+T");
}
uint32_t subghz_frequency_find_correct(uint32_t input) {
@@ -242,7 +251,9 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
bool need_redraw = false;
- if(event->key == InputKeyBack) return false;
+ if(event->key == InputKeyBack) {
+ return false;
+ }
if(((event->type == InputTypePress) || (event->type == InputTypeRepeat)) &&
((event->key == InputKeyLeft) || (event->key == InputKeyRight))) {
@@ -251,61 +262,100 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
switch(event->key) {
case InputKeyLeft:
trigger_level -= TRIGGER_STEP;
- if(trigger_level < RSSI_MIN) trigger_level = RSSI_MIN;
+ if(trigger_level < RSSI_MIN) {
+ trigger_level = RSSI_MIN;
+ }
break;
default:
case InputKeyRight:
trigger_level += TRIGGER_STEP;
- if(trigger_level > RSSI_MAX) trigger_level = RSSI_MAX;
+ if(trigger_level > RSSI_MAX) {
+ trigger_level = RSSI_MAX;
+ }
break;
}
subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, trigger_level);
FURI_LOG_I(TAG, "trigger = %.1f", (double)trigger_level);
need_redraw = true;
- }
-
- if(event->type == InputTypePress && event->key == InputKeyDown) {
+ } else if(event->type == InputTypePress && event->key == InputKeyUp) {
if(instance->feedback_level == 0) {
instance->feedback_level = 2;
} else {
instance->feedback_level--;
}
+#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "feedback_level = %d", instance->feedback_level);
+#endif
+ need_redraw = true;
+ } else if(
+ ((event->type == InputTypePress) || (event->type == InputTypeRepeat)) &&
+ event->key == InputKeyDown) {
+ instance->show_frame = instance->max_index > 0;
+ if(instance->show_frame) {
+ instance->selected_index = (instance->selected_index + 1) % instance->max_index;
+ need_redraw = true;
+ }
+ } else if(event->key == InputKeyOk) {
need_redraw = true;
- }
-
- if(event->key == InputKeyOk) {
bool updated = false;
+ uint32_t frequency_to_save = 0;
with_view_model(
instance->view,
SubGhzFrequencyAnalyzerModel * model,
{
- uint32_t prev_freq_to_save = model->frequency_to_save;
- uint32_t frequency_candidate = 0;
- if(model->frequency != 0) {
- frequency_candidate = model->frequency;
- } else if(model->frequency_last != 0) {
- frequency_candidate = model->frequency_last;
- }
- if(frequency_candidate == 0 ||
- !furi_hal_subghz_is_frequency_valid(frequency_candidate) ||
- prev_freq_to_save == frequency_candidate) {
- frequency_candidate = 0;
- } else {
- frequency_candidate = subghz_frequency_find_correct(frequency_candidate);
- }
- if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) {
+ frequency_to_save = model->frequency_to_save;
+ if(model->show_frame && !model->signal) {
+ uint32_t prev_freq_to_save = model->frequency_to_save;
+ uint32_t frequency_candidate = model->history_frequency[model->selected_index];
+ if(frequency_candidate == 0 ||
+ !furi_hal_subghz_is_frequency_valid(frequency_candidate) ||
+ prev_freq_to_save == frequency_candidate) {
+ frequency_candidate = 0;
+ } else {
+ frequency_candidate = subghz_frequency_find_correct(frequency_candidate);
+ }
+ if(frequency_candidate > 0 &&
+ frequency_candidate != model->frequency_to_save) {
#ifdef FURI_DEBUG
- FURI_LOG_D(
- TAG,
- "frequency_to_save: %ld, candidate: %ld",
- model->frequency_to_save,
- frequency_candidate);
+ FURI_LOG_D(
+ TAG,
+ "frequency_to_save: %ld, candidate: %ld",
+ model->frequency_to_save,
+ frequency_candidate);
#endif
- model->frequency_to_save = frequency_candidate;
- notification_message(instance->notifications, &sequence_saved);
- notification_message(instance->notifications, &sequence_hw_blink);
- updated = true;
+ model->frequency_to_save = frequency_candidate;
+ updated = true;
+ }
+ } else if(model->show_frame && model->signal) {
+ uint32_t prev_freq_to_save = model->frequency_to_save;
+ uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency);
+ if(frequency_candidate == 0 ||
+ !furi_hal_subghz_is_frequency_valid(frequency_candidate) ||
+ prev_freq_to_save == frequency_candidate) {
+ frequency_candidate = 0;
+ } else {
+ frequency_candidate = subghz_frequency_find_correct(frequency_candidate);
+ }
+ if(frequency_candidate > 0 &&
+ frequency_candidate != model->frequency_to_save) {
+ model->frequency_to_save = frequency_candidate;
+ updated = true;
+ }
+ } else if(!model->show_frame && model->signal) {
+ uint32_t prev_freq_to_save = model->frequency_to_save;
+ uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency);
+ if(frequency_candidate == 0 ||
+ !furi_hal_subghz_is_frequency_valid(frequency_candidate) ||
+ prev_freq_to_save == frequency_candidate) {
+ frequency_candidate = 0;
+ } else {
+ frequency_candidate = subghz_frequency_find_correct(frequency_candidate);
+ }
+ if(frequency_candidate > 0 &&
+ frequency_candidate != model->frequency_to_save) {
+ model->frequency_to_save = frequency_candidate;
+ updated = true;
+ }
}
},
true);
@@ -324,17 +374,15 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
}
// First device receive short, then when user release button we get long
- if(event->type == InputTypeLong) {
+ if(event->type == InputTypeLong && frequency_to_save > 0) {
#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Longpress!");
+ FURI_LOG_I(TAG, "Long press!");
#endif
- // Stop blinking
- notification_message(instance->notifications, &sequence_hw_blink_stop);
-
// Stop worker
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
subghz_frequency_analyzer_worker_stop(instance->worker);
}
+
instance->callback(SubGhzCustomEventViewReceiverUnlock, instance->context);
}
}
@@ -346,10 +394,12 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
SubGhzFrequencyAnalyzerModel * model,
{
model->rssi_last = instance->rssi_last;
- model->frequency_last = instance->frequency_last;
model->trigger =
subghz_frequency_analyzer_worker_get_trigger_level(instance->worker);
model->feedback_level = instance->feedback_level;
+ model->max_index = instance->max_index;
+ model->show_frame = instance->show_frame;
+ model->selected_index = instance->selected_index;
},
true);
}
@@ -369,43 +419,91 @@ uint32_t round_int(uint32_t value, uint8_t n) {
return value;
}
-void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) {
- furi_assert(context);
+void subghz_frequency_analyzer_pair_callback(
+ void* context,
+ uint32_t frequency,
+ float rssi,
+ bool signal) {
SubGhzFrequencyAnalyzer* instance = context;
-
if((rssi == 0.f) && (instance->locked)) {
- notification_message(instance->notifications, &sequence_hw_blink);
- instance->frequency_last_vis = instance->frequency_last;
- }
+ if(instance->callback) {
+ instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context);
+ }
+ //update history
+ instance->show_frame = true;
+ uint8_t max_index = instance->max_index;
+ with_view_model(
+ instance->view,
+ SubGhzFrequencyAnalyzerModel * model,
+ {
+ bool in_array = false;
+ uint32_t normal_frequency = subghz_frequency_find_correct(model->frequency);
+ for(size_t i = 0; i < MAX_HISTORY; i++) {
+ if(model->history_frequency[i] == normal_frequency) {
+ in_array = true;
+ if(model->history_frequency[i] > 0) {
+ if(model->history_frequency_rx_count[i] == 0) {
+ model->history_frequency_rx_count[i]++;
+ }
+ model->history_frequency_rx_count[i]++;
+ }
+ if(i > 0) {
+ size_t offset = 0;
+ uint8_t temp_rx_count = model->history_frequency_rx_count[i];
- if((rssi != 0.f) && (frequency != 0)) {
- // Threre is some signal
+ for(size_t j = MAX_HISTORY - 1; j > 0; j--) {
+ if(j == i) {
+ offset++;
+ }
+ model->history_frequency[j] = model->history_frequency[j - offset];
+ model->history_frequency_rx_count[j] =
+ model->history_frequency_rx_count[j - offset];
+ }
+ model->history_frequency[0] = normal_frequency;
+ model->history_frequency_rx_count[0] = temp_rx_count;
+ }
+
+ break;
+ }
+ }
+
+ if(!in_array) {
+ model->history_frequency[3] = model->history_frequency[2];
+ model->history_frequency[2] = model->history_frequency[1];
+ model->history_frequency[1] = model->history_frequency[0];
+ model->history_frequency[0] = normal_frequency;
+
+ model->history_frequency_rx_count[3] = model->history_frequency_rx_count[2];
+ model->history_frequency_rx_count[2] = model->history_frequency_rx_count[1];
+ model->history_frequency_rx_count[1] = model->history_frequency_rx_count[0];
+ model->history_frequency_rx_count[0] = 0;
+ }
+
+ if(max_index < MAX_HISTORY) {
+ for(size_t i = 0; i < MAX_HISTORY; i++) {
+ if(model->history_frequency[i] > 0) {
+ max_index = i + 1;
+ }
+ }
+ }
+ },
+ false);
+ instance->max_index = max_index;
+ } else if((rssi != 0.f) && (!instance->locked)) {
+ // There is some signal
FURI_LOG_I(TAG, "rssi = %.2f, frequency = %ld Hz", (double)rssi, frequency);
frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz
- if(!instance->locked) {
- // Triggered!
- instance->rssi_last = rssi;
- notification_message(instance->notifications, &sequence_hw_blink_stop);
- switch(instance->feedback_level) {
- case 1: // 1 - only vibro
- notification_message(instance->notifications, &sequence_frequency);
- break;
- case 2: // 2 - vibro and beep
- notification_message(instance->notifications, &sequence_success);
- break;
- default: // 0 - no feedback
- notification_message(instance->notifications, &sequence_display_backlight_on);
- break;
- }
+ // Triggered!
+ instance->rssi_last = rssi;
+ if(instance->callback) {
+ instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);
+ }
+ }
- FURI_LOG_D(TAG, "triggered");
- }
- // Update values
- if(rssi >= instance->rssi_last) {
- instance->rssi_last = rssi;
- instance->frequency_last = frequency;
- }
+ // Update values
+ if(rssi >= instance->rssi_last && (frequency != 0)) {
+ instance->rssi_last = rssi;
}
instance->locked = (rssi != 0.f);
@@ -416,9 +514,12 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency,
model->rssi = rssi;
model->rssi_last = instance->rssi_last;
model->frequency = frequency;
- model->frequency_last = instance->frequency_last_vis;
+ model->signal = signal;
model->trigger = subghz_frequency_analyzer_worker_get_trigger_level(instance->worker);
model->feedback_level = instance->feedback_level;
+ model->max_index = instance->max_index;
+ model->show_frame = instance->show_frame;
+ model->selected_index = instance->selected_index;
},
true);
}
@@ -427,10 +528,6 @@ void subghz_frequency_analyzer_enter(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;
- // Notifications
- instance->notifications = furi_record_open(RECORD_NOTIFICATION);
- notification_message(instance->notifications, &sequence_hw_blink);
-
//Start worker
instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context);
@@ -442,18 +539,29 @@ void subghz_frequency_analyzer_enter(void* context) {
subghz_frequency_analyzer_worker_start(instance->worker);
instance->rssi_last = 0;
- instance->frequency_last = 0;
- instance->frequency_last_vis = 0;
- subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, RSSI_MIN);
+ instance->selected_index = 0;
+ instance->max_index = 0;
+ instance->show_frame = false;
+ //subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, RSSI_MIN);
with_view_model(
instance->view,
SubGhzFrequencyAnalyzerModel * model,
{
+ model->selected_index = 0;
+ model->max_index = 0;
+ model->show_frame = false;
model->rssi = 0;
model->rssi_last = 0;
model->frequency = 0;
- model->frequency_last = 0;
+ model->history_frequency[3] = 0;
+ model->history_frequency[2] = 0;
+ model->history_frequency[1] = 0;
+ model->history_frequency[0] = 0;
+ model->history_frequency_rx_count[3] = 0;
+ model->history_frequency_rx_count[2] = 0;
+ model->history_frequency_rx_count[1] = 0;
+ model->history_frequency_rx_count[0] = 0;
model->frequency_to_save = 0;
model->trigger = RSSI_MIN;
},
@@ -464,9 +572,6 @@ void subghz_frequency_analyzer_exit(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;
- // Stop blinking
- notification_message(instance->notifications, &sequence_hw_blink_stop);
-
// Stop worker
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
subghz_frequency_analyzer_worker_stop(instance->worker);
@@ -478,7 +583,6 @@ void subghz_frequency_analyzer_exit(void* context) {
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() {
SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer));
- furi_assert(instance);
instance->feedback_level = 2;
@@ -519,9 +623,9 @@ uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer
return frequency;
}
-uint8_t subghz_frequency_analyzer_feedback_level(
+SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level(
SubGhzFrequencyAnalyzer* instance,
- uint8_t level,
+ SubGHzFrequencyAnalyzerFeedbackLevel level,
bool update) {
furi_assert(instance);
if(update) {
@@ -534,4 +638,9 @@ uint8_t subghz_frequency_analyzer_feedback_level(
}
return instance->feedback_level;
+}
+
+float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance) {
+ furi_assert(instance);
+ return subghz_frequency_analyzer_worker_get_trigger_level(instance->worker);
}
\ No newline at end of file
diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.h b/applications/main/subghz/views/subghz_frequency_analyzer.h
index 196d69c79..95169c08d 100644
--- a/applications/main/subghz/views/subghz_frequency_analyzer.h
+++ b/applications/main/subghz/views/subghz_frequency_analyzer.h
@@ -3,6 +3,12 @@
#include
#include "../helpers/subghz_custom_event.h"
+typedef enum {
+ SubGHzFrequencyAnalyzerFeedbackLevelAll,
+ SubGHzFrequencyAnalyzerFeedbackLevelVibro,
+ SubGHzFrequencyAnalyzerFeedbackLevelMute
+} SubGHzFrequencyAnalyzerFeedbackLevel;
+
typedef struct SubGhzFrequencyAnalyzer SubGhzFrequencyAnalyzer;
typedef void (*SubGhzFrequencyAnalyzerCallback)(SubGhzCustomEvent event, void* context);
@@ -20,7 +26,9 @@ View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* subghz_static)
uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer* instance);
-uint8_t subghz_frequency_analyzer_feedback_level(
+SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level(
SubGhzFrequencyAnalyzer* instance,
- uint8_t level,
+ SubGHzFrequencyAnalyzerFeedbackLevel level,
bool update);
+
+float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance);
\ No newline at end of file
diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c
index 759798f90..f5fdefdd5 100644
--- a/applications/main/subghz/views/subghz_read_raw.c
+++ b/applications/main/subghz/views/subghz_read_raw.c
@@ -23,11 +23,13 @@ typedef struct {
FuriString* sample_write;
FuriString* file_name;
uint8_t* rssi_history;
+ uint8_t rssi_curret;
bool rssi_history_end;
uint8_t ind_write;
uint8_t ind_sin;
SubGhzReadRAWStatus status;
bool raw_send_only;
+ float raw_threshold_rssi;
} SubGhzReadRAWModel;
void subghz_read_raw_set_callback(
@@ -55,21 +57,27 @@ void subghz_read_raw_add_data_statusbar(
true);
}
-void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) {
+void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) {
furi_assert(instance);
uint8_t u_rssi = 0;
- if(rssi < -90) {
+ if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
u_rssi = 0;
} else {
- u_rssi = (uint8_t)((rssi + 90) / 2.7);
+ u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
}
with_view_model(
instance->view,
SubGhzReadRAWModel * model,
{
- model->rssi_history[model->ind_write++] = u_rssi;
+ model->rssi_curret = u_rssi;
+ if(trace) {
+ model->rssi_history[model->ind_write++] = u_rssi;
+ } else {
+ model->rssi_history[model->ind_write] = u_rssi;
+ }
+
if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) {
model->rssi_history_end = true;
model->ind_write = 0;
@@ -188,24 +196,53 @@ void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) {
void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
int ind = 0;
int base = 0;
+ uint8_t width = 2;
if(model->rssi_history_end == false) {
for(int i = model->ind_write; i >= 0; i--) {
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);
}
+ canvas_draw_line(
+ canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret);
if(model->ind_write > 3) {
- canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13);
+ canvas_draw_line(
+ canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret);
+
+ for(uint8_t i = 13; i < 47; i += width * 2) {
+ canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width);
+ }
canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12);
canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13);
}
} else {
+ int i = 0;
base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write;
- for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) {
+ for(i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i > 0; i--) {
ind = i - base;
if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE;
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]);
}
+
canvas_draw_line(
- canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13);
+ canvas,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
+ 47,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
+ 47 - model->rssi_curret);
+ canvas_draw_line(
+ canvas,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
+ 47,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
+ 47 - model->rssi_curret);
+
+ for(uint8_t i = 13; i < 47; i += width * 2) {
+ canvas_draw_line(
+ canvas,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE,
+ i,
+ SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE,
+ i + width);
+ }
canvas_draw_line(
canvas,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2,
@@ -221,6 +258,24 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
}
}
+void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
+ uint8_t x = 118;
+ uint8_t y = 48;
+
+ if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) {
+ uint8_t x = 118;
+ y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
+
+ uint8_t width = 3;
+ for(uint8_t i = 0; i < x; i += width * 2) {
+ canvas_draw_line(canvas, i, y, i + width, y);
+ }
+ }
+ canvas_draw_line(canvas, x, y - 2, x, y + 2);
+ canvas_draw_line(canvas, x - 1, y - 1, x - 1, y + 1);
+ canvas_draw_dot(canvas, x - 2, y);
+}
+
void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
uint8_t graphics_mode = 1;
canvas_set_color(canvas, ColorBlack);
@@ -281,8 +336,9 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
} else {
subghz_read_raw_draw_rssi(canvas, model);
subghz_read_raw_draw_scale(canvas, model);
+ subghz_read_raw_draw_threshold_rssi(canvas, model);
canvas_set_font_direction(canvas, CanvasDirectionBottomToTop);
- canvas_draw_str(canvas, 126, 40, "RSSI");
+ canvas_draw_str(canvas, 128, 40, "RSSI");
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
}
}
@@ -440,7 +496,8 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
void subghz_read_raw_set_status(
SubGhzReadRAW* instance,
SubGhzReadRAWStatus status,
- const char* file_name) {
+ const char* file_name,
+ float raw_threshold_rssi) {
furi_assert(instance);
switch(status) {
@@ -454,6 +511,7 @@ void subghz_read_raw_set_status(
model->ind_write = 0;
furi_string_reset(model->file_name);
furi_string_set(model->sample_write, "0 spl.");
+ model->raw_threshold_rssi = raw_threshold_rssi;
},
true);
break;
@@ -544,6 +602,7 @@ SubGhzReadRAW* subghz_read_raw_alloc(bool raw_send_only) {
model->file_name = furi_string_alloc();
model->raw_send_only = raw_send_only;
model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));
+ model->raw_threshold_rssi = -127.0f;
},
true);
diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h
index a4e16ca98..43afac427 100644
--- a/applications/main/subghz/views/subghz_read_raw.h
+++ b/applications/main/subghz/views/subghz_read_raw.h
@@ -3,6 +3,8 @@
#include
#include "../helpers/subghz_custom_event.h"
+#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+
typedef struct SubGhzReadRAW SubGhzReadRAW;
typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context);
@@ -40,11 +42,12 @@ void subghz_read_raw_stop_send(SubGhzReadRAW* instance);
void subghz_read_raw_update_sin(SubGhzReadRAW* instance);
-void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi);
+void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace);
void subghz_read_raw_set_status(
SubGhzReadRAW* instance,
SubGhzReadRAWStatus status,
- const char* file_name);
+ const char* file_name,
+ float raw_threshold_rssi);
View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static);
diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam
index 5e825c1bf..d2e695a3f 100644
--- a/applications/main/u2f/application.fam
+++ b/applications/main/u2f/application.fam
@@ -13,4 +13,6 @@ App(
order=80,
fap_icon="U2FIcon.png",
fap_category="Main",
+ fap_icon_assets="images",
+ fap_libs=["assets"],
)
diff --git a/applications/main/u2f/images/ActiveConnection_50x64.png b/applications/main/u2f/images/ActiveConnection_50x64.png
new file mode 100644
index 000000000..1d7686ddd
Binary files /dev/null and b/applications/main/u2f/images/ActiveConnection_50x64.png differ
diff --git a/applications/main/u2f/images/Auth_62x31.png b/applications/main/u2f/images/Auth_62x31.png
new file mode 100644
index 000000000..40f094ac9
Binary files /dev/null and b/applications/main/u2f/images/Auth_62x31.png differ
diff --git a/applications/main/u2f/images/Connect_me_62x31.png b/applications/main/u2f/images/Connect_me_62x31.png
new file mode 100644
index 000000000..68c48c0e6
Binary files /dev/null and b/applications/main/u2f/images/Connect_me_62x31.png differ
diff --git a/applications/main/u2f/images/Connected_62x31.png b/applications/main/u2f/images/Connected_62x31.png
new file mode 100644
index 000000000..eeaf660b1
Binary files /dev/null and b/applications/main/u2f/images/Connected_62x31.png differ
diff --git a/applications/main/u2f/images/Drive_112x35.png b/applications/main/u2f/images/Drive_112x35.png
new file mode 100644
index 000000000..6f7b9c834
Binary files /dev/null and b/applications/main/u2f/images/Drive_112x35.png differ
diff --git a/applications/main/u2f/images/Error_62x31.png b/applications/main/u2f/images/Error_62x31.png
new file mode 100644
index 000000000..bb280e751
Binary files /dev/null and b/applications/main/u2f/images/Error_62x31.png differ
diff --git a/applications/main/u2f/images/SDQuestion_35x43.png b/applications/main/u2f/images/SDQuestion_35x43.png
new file mode 100644
index 000000000..9b9c9a58e
Binary files /dev/null and b/applications/main/u2f/images/SDQuestion_35x43.png differ
diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h
index 53647859a..c7bac40b4 100644
--- a/applications/main/u2f/u2f_app_i.h
+++ b/applications/main/u2f/u2f_app_i.h
@@ -4,6 +4,7 @@
#include "scenes/u2f_scene.h"
#include
+#include
#include
#include
#include
diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c
index 4922d6a5a..6e1a51f33 100644
--- a/applications/main/u2f/u2f_hid.c
+++ b/applications/main/u2f/u2f_hid.c
@@ -58,13 +58,13 @@ struct U2fHid_packet {
struct U2fHid {
FuriThread* thread;
FuriTimer* lock_timer;
- struct U2fHid_packet packet;
uint8_t seq_id_last;
uint16_t req_buf_ptr;
uint32_t req_len_left;
uint32_t lock_cid;
bool lock;
U2fData* u2f_instance;
+ struct U2fHid_packet packet;
};
static void u2f_hid_event_callback(HidU2fEvent ev, void* context) {
@@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) {
WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,
FuriFlagWaitAny,
FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
+ furi_check(!(flags & FuriFlagError));
if(flags & WorkerEvtStop) break;
if(flags & WorkerEvtConnect) {
u2f_set_state(u2f_hid->u2f_instance, 1);
@@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) {
}
if(flags & WorkerEvtRequest) {
uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);
- if(len_cur > 0) {
+ do {
+ if(len_cur == 0) {
+ break;
+ }
if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {
+ if(len_cur < 7) {
+ u2f_hid->req_len_left = 0;
+ break; // Wrong chunk len
+ }
// Init packet
u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);
+ if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) {
+ u2f_hid->req_len_left = 0;
+ break; // Wrong packet len
+ }
if(u2f_hid->packet.len > (len_cur - 7)) {
u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);
len_cur = len_cur - 7;
@@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) {
u2f_hid->req_buf_ptr = len_cur;
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
} else {
+ if(len_cur < 5) {
+ u2f_hid->req_len_left = 0;
+ break; // Wrong chunk len
+ }
// Continuation packet
if(u2f_hid->req_len_left > 0) {
uint32_t cid_temp = 0;
@@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) {
u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);
}
}
- }
+ } while(0);
}
if(flags & WorkerEvtUnlock) {
u2f_hid->lock = false;
@@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) {
u2f_hid->u2f_instance = u2f_inst;
- u2f_hid->thread = furi_thread_alloc();
- furi_thread_set_name(u2f_hid->thread, "U2fHidWorker");
- furi_thread_set_stack_size(u2f_hid->thread, 2048);
- furi_thread_set_context(u2f_hid->thread, u2f_hid);
- furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker);
+ u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid);
furi_thread_start(u2f_hid->thread);
return u2f_hid;
}
diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c
index fa3d6cc24..ecb276a3e 100644
--- a/applications/main/u2f/views/u2f_view.c
+++ b/applications/main/u2f/views/u2f_view.c
@@ -1,5 +1,6 @@
#include "u2f_view.h"
#include
+#include
struct U2fView {
View* view;
@@ -36,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
} else if(model->display_msg == U2fMsgSuccess) {
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
canvas_draw_str_aligned(
- canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!");
+ canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!");
} else if(model->display_msg == U2fMsgError) {
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);
- canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error");
+ canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error");
}
}
diff --git a/applications/main/unirfremix/application.fam b/applications/main/unirfremix/application.fam
index b7b1e59e5..73c063197 100644
--- a/applications/main/unirfremix/application.fam
+++ b/applications/main/unirfremix/application.fam
@@ -1,13 +1,14 @@
App(
appid="SubGHz_Remote",
name="Sub-GHz Remote",
- apptype=FlipperAppType.APP,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="unirfremix_app",
cdefines=["APP_UNIRFREMIX"],
requires=["storage", "gui", "dialogs", "subghz"],
icon="A_UniRFRemix_14",
stack_size=4 * 1024,
order=11,
- # fap_icon="unirfIcon.png",
- # fap_category="Main",
+ fap_icon="unirfIcon.png",
+ fap_category="Main",
+ fap_icon_assets="images",
)
diff --git a/applications/main/unirfremix/images/ButtonDown_7x4.png b/applications/main/unirfremix/images/ButtonDown_7x4.png
new file mode 100644
index 000000000..2954bb6a6
Binary files /dev/null and b/applications/main/unirfremix/images/ButtonDown_7x4.png differ
diff --git a/applications/main/unirfremix/images/ButtonLeft_4x7.png b/applications/main/unirfremix/images/ButtonLeft_4x7.png
new file mode 100644
index 000000000..0b4655d43
Binary files /dev/null and b/applications/main/unirfremix/images/ButtonLeft_4x7.png differ
diff --git a/applications/main/unirfremix/images/ButtonRight_4x7.png b/applications/main/unirfremix/images/ButtonRight_4x7.png
new file mode 100644
index 000000000..8e1c74c1c
Binary files /dev/null and b/applications/main/unirfremix/images/ButtonRight_4x7.png differ
diff --git a/applications/main/unirfremix/images/ButtonUp_7x4.png b/applications/main/unirfremix/images/ButtonUp_7x4.png
new file mode 100644
index 000000000..1be79328b
Binary files /dev/null and b/applications/main/unirfremix/images/ButtonUp_7x4.png differ
diff --git a/applications/main/unirfremix/images/Ok_btn_9x9.png b/applications/main/unirfremix/images/Ok_btn_9x9.png
new file mode 100644
index 000000000..9a1539da2
Binary files /dev/null and b/applications/main/unirfremix/images/Ok_btn_9x9.png differ
diff --git a/applications/main/unirfremix/images/Pin_arrow_down_7x9.png b/applications/main/unirfremix/images/Pin_arrow_down_7x9.png
new file mode 100644
index 000000000..9687397af
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_down_7x9.png differ
diff --git a/applications/main/unirfremix/images/Pin_arrow_left_9x7.png b/applications/main/unirfremix/images/Pin_arrow_left_9x7.png
new file mode 100644
index 000000000..fb4ded78f
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_left_9x7.png differ
diff --git a/applications/main/unirfremix/images/Pin_arrow_right_9x7.png b/applications/main/unirfremix/images/Pin_arrow_right_9x7.png
new file mode 100644
index 000000000..97648d176
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_right_9x7.png differ
diff --git a/applications/main/unirfremix/images/Pin_arrow_up_7x9.png b/applications/main/unirfremix/images/Pin_arrow_up_7x9.png
new file mode 100644
index 000000000..a91a6fd5e
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_up_7x9.png differ
diff --git a/applications/main/unirfremix/images/Pin_cell_13x13.png b/applications/main/unirfremix/images/Pin_cell_13x13.png
new file mode 100644
index 000000000..1b1ff0c2f
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_cell_13x13.png differ
diff --git a/applications/main/unirfremix/images/Pin_star_7x7.png b/applications/main/unirfremix/images/Pin_star_7x7.png
new file mode 100644
index 000000000..42fdea86e
Binary files /dev/null and b/applications/main/unirfremix/images/Pin_star_7x7.png differ
diff --git a/applications/main/unirfremix/images/back_10px.png b/applications/main/unirfremix/images/back_10px.png
new file mode 100644
index 000000000..f9c615a99
Binary files /dev/null and b/applications/main/unirfremix/images/back_10px.png differ
diff --git a/applications/main/unirfremix/images/sub1_10px.png b/applications/main/unirfremix/images/sub1_10px.png
new file mode 100644
index 000000000..5a25fdf4e
Binary files /dev/null and b/applications/main/unirfremix/images/sub1_10px.png differ
diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/unirfremix/unirfremix_app.c
index b9f7aa5e6..cb1fd7871 100644
--- a/applications/main/unirfremix/unirfremix_app.c
+++ b/applications/main/unirfremix/unirfremix_app.c
@@ -8,23 +8,43 @@
#include
#include
-#include
-
#include
#include
#include
#include
-#include