diff --git a/.github/assets/dark_theme_banner.png b/.github/assets/dark_theme_banner.png deleted file mode 100644 index 10a4e8c30..000000000 Binary files a/.github/assets/dark_theme_banner.png and /dev/null differ diff --git a/.github/assets/light_theme_banner.png b/.github/assets/light_theme_banner.png deleted file mode 100644 index 6c6b3b708..000000000 Binary files a/.github/assets/light_theme_banner.png and /dev/null differ diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md new file mode 100644 index 000000000..1dcbf31ee --- /dev/null +++ b/.github/workflow_data/release.md @@ -0,0 +1,22 @@ +## ⬇️ Download +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.tgz) [recommended] + +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.zip) + +>### [🖥️ Web Updater (chrome)](https://lab.flipper.net/?url={webupdater_url}&channel=XFW-Updater&version={release_tag}) [not recommended] + +**Remember to delete your `apps` (and `update` if using "Install from file...") folders before updating!**\ +**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions!** + +## 🚀 Changelog +{changelog} + +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Direct transfer to my bank](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Patreon](https://patreon.com/CynthiaLabs)** +- **[Paypal](https://paypal.me/RdX2020)** +- **Monero**: 41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T + +**Thanks for all your support <3** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7eee574a..715070b83 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,22 @@ -name: 'Build' +name: 'Build artifact' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: env: - TARGETS: f7 f18 + TARGETS: f7 DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /home/runner/work jobs: - main: + build: runs-on: ubuntu-latest steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - name: 'Checkout code' uses: actions/checkout@v3 @@ -30,70 +24,24 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Get commit details' - id: names - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT - echo "event_type=$TYPE" >> $GITHUB_OUTPUT - - - name: 'Make artifacts directory' - run: | - rm -rf artifacts - mkdir artifacts - - - name: 'Bundle scripts' - run: | - tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug - - name: 'Build the firmware' run: | set -e for TARGET in ${TARGETS}; do TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET copro_dist updater_package \ - ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done - - - name: 'Move upload files' - run: | - set -e - for TARGET in ${TARGETS}; do - mv dist/${TARGET}-*/* artifacts/ + ./fbt TARGET_HW=$TARGET updater_package done - name: "Check for uncommitted changes" run: | git diff --exit-code - - name: 'Bundle resources' - run: | - tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - - - name: 'Bundle core2 firmware' - run: | - cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: name: updater path: | - artifacts/f7-* - - - name: 'Firmware artifact' - uses: actions/upload-artifact@v3 - with: - name: firmware - path: | - artifacts + dist/${{ env.DEFAULT_TARGET }}-* # - name: 'Find Previous Comment' # if: ${{ github.event.pull_request }} @@ -102,7 +50,7 @@ jobs: # with: # issue-number: ${{ github.event.pull_request.number }} # comment-author: 'github-actions[bot]' - # body-includes: 'Compiled firmware for commit' + # body-includes: 'Compiled firmware:' # - name: Artifact info # id: artifact-info @@ -117,44 +65,6 @@ jobs: # comment-id: ${{ steps.fc.outputs.comment-id }} # issue-number: ${{ github.event.pull_request.number }} # body: | - # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + # **Compiled firmware:** # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) # edit-mode: replace - - compact: - if: ${{ !startsWith(github.ref, 'refs/tags') }} - runs-on: ubuntu-latest - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ] - then - git submodule status \ - || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: true - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Build the firmware' - run: | - set -e - for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package - done diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index d1a1a64c3..ad63f711b 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -1,10 +1,10 @@ -name: 'Check submodules branch' +name: 'Check submodules' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: @@ -13,11 +13,6 @@ jobs: check_protobuf: runs-on: ubuntu-latest steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - name: 'Checkout code' uses: actions/checkout@v3 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..818aa7d84 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,27 @@ +name: 'Lint formatting' + +on: + push: + branches: + - dev + - main + tags: + - '*' + pull_request: + +env: + SET_GH_OUTPUT: 1 + +jobs: + lint: + runs-on: ubuntu-latest + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check code formatting' + run: ./fbt lint lint_py diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml deleted file mode 100644 index 232e3c689..000000000 --- a/.github/workflows/lint_c.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: 'Lint C/C++ with clang-format' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - SET_GH_OUTPUT: 1 - -jobs: - lint_c_cpp: - runs-on: ubuntu-latest - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Check code formatting' - id: syntax_check - run: ./fbt lint - - - name: Report code formatting errors - if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - Please fix following code formatting errors: - ``` - ${{ steps.syntax_check.outputs.errors }} - ``` - You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml deleted file mode 100644 index 65a8b6150..000000000 --- a/.github/workflows/pvs_studio.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: 'Static C/C++ analysis with PVS-Studio' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /runner/_work - -jobs: - analyse_c_cpp: - if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: [self-hosted, FlipperZeroShell] - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - id: names - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Supply PVS credentials' - run: | - pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} - - - name: 'Convert PVS-Studio output to html and detect warnings' - id: pvs-warn - run: | - WARNINGS=0 - ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 - echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - - name: 'Upload report' - if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" - source: "./build/f7-firmware-DC/pvsreport" - dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" - flags: "--recursive --acl public-read" - - - name: 'Find Previous Comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/find-comment@v2 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'PVS-Studio report for commit' - - - name: 'Create or update comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) - edit-mode: replace - - - name: 'Raise exception' - if: ${{ steps.pvs-warn.outputs.warnings != 0 }} - run: | - echo "Please fix all PVS warnings before merge" - exit 1 - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..dc6ead78d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +name: "Release integration" + +on: + pull_request_review: + types: [submitted] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + release: + if: | + github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name && + endsWith(github.event.pull_request.title, ' Release Candidate Changes') && + github.event.review.author_association == 'OWNER' && + startsWith(github.event.pull_request.title, 'V') && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.head.ref == 'dev' && + github.event.pull_request.state == 'open' && + github.event.pull_request.draft == false && + github.event.review.state == 'APPROVED' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: "Update version number" + run: | + VERSION="$(python -c "print('%04d' % int('${{ github.event.pull_request.title }}'.removeprefix('V').removesuffix(' Release Candidate Changes')), end='')")" + DATE="$(python -c "from datetime import date;print(date.today().strftime('%d%m%Y'), end='')")" + RELEASE_NAME="XFW-${VERSION}" + RELEASE_TAG="${RELEASE_NAME}_${DATE}" + echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV + + sed "s/VERSION = \"XFW-[[:digit:]]\{4\}\"/VERSION = \"${RELEASE_NAME}\"/g" ./scripts/version.py > ./scripts/version.py.new + rm ./scripts/version.py + mv ./scripts/version.py.new ./scripts/version.py + + sed "s/DIST_SUFFIX = \"XFW-[[:digit:]]\{4\}_[[:digit:]]\{8\}\"/DIST_SUFFIX = \"${RELEASE_TAG}\"/g" ./fbt_options.py > ./fbt_options.py.new + rm ./fbt_options.py + mv ./fbt_options.py.new ./fbt_options.py + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET updater_package + done + + - name: "Make tgz, zip and webupdater" + run: | + cd ./dist/${DEFAULT_TARGET}-* + mv flipper-z-${DEFAULT_TARGET}-update-*.tgz ${RELEASE_TAG}.tgz + cd ./${DEFAULT_TARGET}-update-* + 7z a ../${RELEASE_TAG}.zip . + cd .. + python -m pip install pyncclient + WEBUPDATER_URL="$(python -c "import nextcloud_client as n;c = n.Client('https://cloud.cynthialabs.net/');c.login('${{ secrets.NC_USER }}', '${{ secrets.NC_PASS }}');c.put_file('XFW-Updater/${RELEASE_TAG}.tgz', '${RELEASE_TAG}.tgz');print(c.share_file_with_link('XFW-Updater/${RELEASE_TAG}.tgz').get_link().rstrip('/') + '/download/${RELEASE_TAG}.tgz', end='')")" + echo "WEBUPDATER_URL=${WEBUPDATER_URL}" >> $GITHUB_ENV + + - name: "Update release notes" + run: | + python -c "import json, os;f = '.github/workflow_data/release.md';c = json.load(open(os.environ['GITHUB_EVENT_PATH']))['pull_request']['body'];f_ = open(f);n = f_.read().format(release_tag='${RELEASE_TAG}', webupdater_url='${WEBUPDATER_URL}', changelog=c);f_.close();f_ = open(f, 'w');f_.write(n);f_.close()" + + - name: "Commit new version number" + uses: EndBug/add-and-commit@v9 + with: + default_author: github_actions + message: Update version number + add: './fbt_options.py ./scripts/version.py' + + - name: "Merge pull request" + uses: "pascalgn/automerge-action@v0.15.6" + env: + MERGE_LABELS: "" + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: "Make release" + uses: softprops/action-gh-release@v1 + with: + body_path: ".github/workflow_data/release.md" + draft: false + prerelease: false + files: | + ${{ env.RELEASE_TAG }}.tgz + ${{ env.RELEASE_TAG }}.zip + name: "${{ env.RELEASE_TAG }}" + tag_name: "${{ env.RELEASE_TAG }}" + target_commitish: main diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 5d0ee1eef..7f3d311b0 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -1,33 +1,36 @@ -name: SonarCloud +name: 'SonarCloud analysis' + on: workflow_dispatch: pull_request: types: [opened, synchronize, reopened] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + jobs: - build: - name: Build and analyze + analyze: runs-on: ubuntu-latest env: SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: "$HOME/.sonar/build_wrapper_output" # Directory where build-wrapper output will be placed FBT_NO_SYNC: "true" - TARGETS: f7 - DEFAULT_TARGET: f7 steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - uses: actions/checkout@v2 + + - name: 'Checkout code' + uses: actions/checkout@v3 with: + submodules: 'recursive' # FBT_NO_SYNC is on, get submodules now fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - submodules: 'recursive' + ref: ${{ github.event.pull_request.head.sha }} + - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 + - name: Download and set up sonar-scanner env: SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip @@ -36,6 +39,7 @@ jobs: curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH + - name: Download and set up build-wrapper env: BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip @@ -43,10 +47,16 @@ jobs: curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + - name: Run build-wrapper run: | - mkdir $HOME/.sonar/build_wrapper_output - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build + mkdir ${{ env.BUILD_WRAPPER_OUT_DIR }} + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET updater_package" + done + - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 025246faa..f816372fa 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,10 @@ Brewfile.lock.json # Visual Studio .vs/ +# Kate +.kateproject +.kateconfig + # legendary cmake's build CMakeLists.txt @@ -68,9 +72,8 @@ PVS-Studio.log # Automate files, etc automate.py deployments/ -fbt_options.py commitnotes.md -lib/STM32CubeWB +fbt_options.py # Asset packs assets/dolphin/custom/* diff --git a/.gitmodules b/.gitmodules index a97e0933a..de580c3c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,6 +28,9 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git +[submodule "applications/plugins/subbrute"] + path = applications/plugins/subbrute + url = https://github.com/derskythe/flipperzero-subbrute.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 a9ab9c9f6..49c63ad73 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -44,3 +44,6 @@ # Functions that always return the same error code //-V:picopass_device_decrypt:1048 + +# Examples +//V_EXCLUDE_PATH applications/examples/ \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 5c46d3979..f7a9f8269 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -11,9 +11,10 @@ "args": { "useSingleResult": true, "env": { - "PATH": "${workspaceFolder};${env:PATH}" + "PATH": "${workspaceFolder};${env:PATH}", + "FBT_QUIET": 1 }, - "command": "./fbt get_blackmagic", + "command": "fbt get_blackmagic", "description": "Get Blackmagic device", } } diff --git a/ReadMe.md b/ReadMe.md index 6ff014479..bfce45ed6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,13 +1,14 @@
Xtreme Firmware for the Flipper Zero
+
-Base Graphics: Change the fallback assets used. Its either SFW (default) or NSFW
-Asset Pack: Allows you to easily customize your firmware, more on that below
-Anim Speed: Speed in which the animations play
-Cycle Anims: Duration of how long animations are played before switching to next
-Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;)
-Battery Icon: Classic Firmware battery style toggle, just at a more convenient place
-XP Level: Changes your Flippers level
-SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned
-SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper
+Graphics:Statusbar:Protocols:Dolphin:Misc:Anims & Icons folders, inside each Asset Pack.
+You can easily create your own pack, or find some user made ones in the discord channel. Check here for a tutorial on creating your own. Essentially, we got our own Anims & Icons folders, inside each Asset Pack.
SD/dolphin_custom
-After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets & animations!
+After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets for all apps (e.g. Subghz scanning asset) & animations!
+-----
+
+Bad Keyboard:
+
+
+
+BadUSB is a wonderful app, but it lacks bluetooth capabilities. Now some might argue that its useless as you will always need authentication from both sides, but what if I told you that we found a solution to this problem?
+
+Bad-KB allows you to toggle between USB and Bluetooth mode for your attacks. In Bluetooth mode it allows you to spoof the name & MAC of the device to whatever you want. Being a JBL speaker or a wireless razer keyboard is easily doable, allowing you to trick people so you can run your payloads without needing a cable at hand.
+
-----
Levels:
@@ -86,7 +91,7 @@ With this new system in place, it allows for some cool stuff like locking animat
Our example
-In our case, this is used with the NSFW animations. Dont worry, these are disabled by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time. (Funfact for those reading.. thats why the repository has this warning. Github doesnt like my tits :c)
+In our example case, this is used with the NSFW animation pack you can select in the Xtreme app. Dont worry, this is not used by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time.
| Level | Animations |
| ------------- | ------------- |
@@ -108,6 +113,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Xtreme App
- Asset Packs
- More UI options
+- Bad-Keyboard App
- A new battery display-type
- Scrolling view for long file names in browser
- NSFW Animations tied to the level system. Read more above
@@ -117,7 +123,6 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Multiple NFC protocols
- Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX
- Subghz and IR signal replication via gpio | Credits to @ankris812
-- Honda Keys (CVE-2022-27254) & Ford blockers
- New API Routes for Locale settings
```
@@ -129,12 +134,13 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Tons of apps
- File browser
- Massive compiler re-do
-- About 1k files to speed things up a lot
+- About 4k files to speed things up a lot
- Applications to now use the new Locale setting
```
```txt
[Fixed]
+- Keyboard issues on first char
- Passport crash on high level
- SFW / Dummy_mode getting you XP
- Leveling system
@@ -152,17 +158,21 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
Install:
-**NOTE: If you are coming from a different FW, it is recommended to delete / clear your "apps" folder on the SD card prior to updating. This folder houses all the .fap files, which do not update to the correct API versions by default if old ones are present (Thanks flipper devs). This does `NOT` remove any of your saved files!**
+**This is the recommended install procedure. Please follow these steps EXACTLY and CAREFULLY to ensure you install correctly.**
+**This process will NOT delete any saved files and simply ensures the install goes smoothly.**
-- Download the latest release (.zip) from [The releases tab](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest)
-- Extract the archive. This is now your new Firmware folder
-- Open [qFlipper](https://flipperzero.one/update), head to `SD/Update` and simply move the firmware folder there
-- On the Flipper, hit the `Arrow Down` button, this will get you to the file menu. Hit `Arrow Left` once, and then simply search for your updates folder
-- Inside that folder, select the Firmware you just moved onto it, and run the file thats simply called `Update`
+- Download the latest release (`.tgz`) from [the release page](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest)
+- Open [qFlipper](https://flipperzero.one/update) and connect your Flipper Zero
+- Go to the files tab, into SD Card, and DELETE THE `apps` AND `update` FOLDERS
+- Go back to the device tab
+- Click `Install from file...` and select the downloaded file (`.tgz`)
- Enjoy!
+**If you have issues or crashes with that process, you can try to use `Settings > Storage > Factory Reset` then retry the install.**
+**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.**
+
----
Build it yourself:
diff --git a/SConstruct b/SConstruct
index 62e37dfdc..81ff67790 100644
--- a/SConstruct
+++ b/SConstruct
@@ -139,7 +139,7 @@ if GetOption("fullenv") or any(
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
distenv.Default(basic_dist)
-dist_dir = distenv.GetProjetDirName()
+dist_dir = distenv.GetProjectDirName()
fap_dist = [
distenv.Install(
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
diff --git a/applications/ReadMe.md b/applications/ReadMe.md
index efc9afd86..ddbe5d65b 100644
--- a/applications/ReadMe.md
+++ b/applications/ReadMe.md
@@ -86,4 +86,4 @@ Small applications providing configuration for basic firmware and its services.
Utility apps not visible in other menus.
- `storage_move_to_sd` - Data migration tool for internal storage
-- `updater` - updater service & application
+- `updater` - Update service & application
diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp
index 9d3708ebe..337437d0e 100644
--- a/applications/debug/accessor/accessor_app.cpp
+++ b/applications/debug/accessor/accessor_app.cpp
@@ -34,7 +34,7 @@ void AccessorApp::run(void) {
AccessorApp::AccessorApp()
: text_store{0} {
notification = static_cast(furi_record_open(RECORD_NOTIFICATION));
- onewire_host = onewire_host_alloc();
+ onewire_host = onewire_host_alloc(&ibutton_gpio);
furi_hal_power_enable_otg();
}
diff --git a/applications/debug/keypad_test/keypad_test.c b/applications/debug/keypad_test/keypad_test.c
index 2470baf8d..9e8881def 100644
--- a/applications/debug/keypad_test/keypad_test.c
+++ b/applications/debug/keypad_test/keypad_test.c
@@ -11,6 +11,7 @@ typedef struct {
uint16_t left;
uint16_t right;
uint16_t ok;
+ FuriMutex* mutex;
} KeypadTestState;
static void keypad_test_reset_state(KeypadTestState* state) {
@@ -22,7 +23,8 @@ static void keypad_test_reset_state(KeypadTestState* state) {
}
static void keypad_test_render_callback(Canvas* canvas, void* ctx) {
- KeypadTestState* state = (KeypadTestState*)acquire_mutex((ValueMutex*)ctx, 25);
+ KeypadTestState* state = ctx;
+ furi_mutex_acquire(state->mutex, FuriWaitForever);
canvas_clear(canvas);
char strings[5][20];
@@ -51,7 +53,7 @@ static void keypad_test_render_callback(Canvas* canvas, void* ctx) {
canvas_draw_str(canvas, 10, 63, "[back] - reset, hold to exit");
- release_mutex((ValueMutex*)ctx, state);
+ furi_mutex_release(state->mutex);
}
static void keypad_test_input_callback(InputEvent* input_event, void* ctx) {
@@ -64,17 +66,17 @@ int32_t keypad_test_app(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
furi_check(event_queue);
- KeypadTestState _state = {{false, false, false, false, false}, 0, 0, 0, 0, 0};
+ KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL};
+ state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) {
+ if(!state.mutex) {
FURI_LOG_E(TAG, "cannot create mutex");
return 0;
}
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, keypad_test_render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, keypad_test_render_callback, &state);
view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue);
// Open GUI and register view_port
@@ -83,7 +85,7 @@ int32_t keypad_test_app(void* p) {
InputEvent event;
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
- KeypadTestState* state = (KeypadTestState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(state.mutex, FuriWaitForever);
FURI_LOG_I(
TAG,
"key: %s type: %s",
@@ -92,54 +94,54 @@ int32_t keypad_test_app(void* p) {
if(event.key == InputKeyRight) {
if(event.type == InputTypePress) {
- state->press[0] = true;
+ state.press[0] = true;
} else if(event.type == InputTypeRelease) {
- state->press[0] = false;
+ state.press[0] = false;
} else if(event.type == InputTypeShort) {
- ++state->right;
+ ++state.right;
}
} else if(event.key == InputKeyLeft) {
if(event.type == InputTypePress) {
- state->press[1] = true;
+ state.press[1] = true;
} else if(event.type == InputTypeRelease) {
- state->press[1] = false;
+ state.press[1] = false;
} else if(event.type == InputTypeShort) {
- ++state->left;
+ ++state.left;
}
} else if(event.key == InputKeyUp) {
if(event.type == InputTypePress) {
- state->press[2] = true;
+ state.press[2] = true;
} else if(event.type == InputTypeRelease) {
- state->press[2] = false;
+ state.press[2] = false;
} else if(event.type == InputTypeShort) {
- ++state->up;
+ ++state.up;
}
} else if(event.key == InputKeyDown) {
if(event.type == InputTypePress) {
- state->press[3] = true;
+ state.press[3] = true;
} else if(event.type == InputTypeRelease) {
- state->press[3] = false;
+ state.press[3] = false;
} else if(event.type == InputTypeShort) {
- ++state->down;
+ ++state.down;
}
} else if(event.key == InputKeyOk) {
if(event.type == InputTypePress) {
- state->press[4] = true;
+ state.press[4] = true;
} else if(event.type == InputTypeRelease) {
- state->press[4] = false;
+ state.press[4] = false;
} else if(event.type == InputTypeShort) {
- ++state->ok;
+ ++state.ok;
}
} else if(event.key == InputKeyBack) {
if(event.type == InputTypeLong) {
- release_mutex(&state_mutex, state);
+ furi_mutex_release(state.mutex);
break;
} else if(event.type == InputTypeShort) {
- keypad_test_reset_state(state);
+ keypad_test_reset_state(&state);
}
}
- release_mutex(&state_mutex, state);
+ furi_mutex_release(state.mutex);
view_port_update(view_port);
}
@@ -147,7 +149,7 @@ int32_t keypad_test_app(void* p) {
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(state.mutex);
furi_record_close(RECORD_GUI);
diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c
index 46248cf3c..003df55dc 100644
--- a/applications/debug/locale_test/locale_test.c
+++ b/applications/debug/locale_test/locale_test.c
@@ -99,4 +99,4 @@ int32_t locale_test_app(void* p) {
view_dispatcher_run(app->view_dispatcher);
locale_test_free(app);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/debug/text_box_test/text_box_test.c b/applications/debug/text_box_test/text_box_test.c
index d7194ffee..b980f686e 100644
--- a/applications/debug/text_box_test/text_box_test.c
+++ b/applications/debug/text_box_test/text_box_test.c
@@ -53,15 +53,17 @@ static void (*text_box_test_render[])(Canvas* canvas) = {
typedef struct {
uint32_t idx;
+ FuriMutex* mutex;
} TextBoxTestState;
static void text_box_test_render_callback(Canvas* canvas, void* ctx) {
- TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25);
+ TextBoxTestState* state = ctx;
+ furi_mutex_acquire(state->mutex, FuriWaitForever);
canvas_clear(canvas);
text_box_test_render[state->idx](canvas);
- release_mutex((ValueMutex*)ctx, state);
+ furi_mutex_release(state->mutex);
}
static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
@@ -74,17 +76,17 @@ int32_t text_box_test_app(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
furi_check(event_queue);
- TextBoxTestState _state = {.idx = 0};
+ TextBoxTestState state = {.idx = 0, .mutex = NULL};
+ state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) {
+ if(!state.mutex) {
FURI_LOG_E(TAG, "Cannot create mutex");
return 0;
}
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, text_box_test_render_callback, &state);
view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue);
// Open GUI and register view_port
@@ -94,24 +96,24 @@ int32_t text_box_test_app(void* p) {
uint32_t test_renders_num = COUNT_OF(text_box_test_render);
InputEvent event;
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
- TextBoxTestState* state = acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(state.mutex, FuriWaitForever);
if(event.type == InputTypeShort) {
if(event.key == InputKeyRight) {
- if(state->idx < test_renders_num - 1) {
- state->idx++;
+ if(state.idx < test_renders_num - 1) {
+ state.idx++;
}
} else if(event.key == InputKeyLeft) {
- if(state->idx > 0) {
- state->idx--;
+ if(state.idx > 0) {
+ state.idx--;
}
} else if(event.key == InputKeyBack) {
- release_mutex(&state_mutex, state);
+ furi_mutex_release(state.mutex);
break;
}
}
- release_mutex(&state_mutex, state);
+ furi_mutex_release(state.mutex);
view_port_update(view_port);
}
@@ -119,7 +121,7 @@ int32_t text_box_test_app(void* p) {
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(state.mutex);
furi_record_close(RECORD_GUI);
diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam
index a16c611bd..b9766254c 100644
--- a/applications/debug/uart_echo/application.fam
+++ b/applications/debug/uart_echo/application.fam
@@ -1,6 +1,6 @@
App(
appid="UART_Echo",
- name="UART Echo",
+ name="[GPIO] UART Echo",
apptype=FlipperAppType.PLUGIN,
entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
@@ -8,5 +8,5 @@ App(
stack_size=2 * 1024,
order=70,
fap_icon="uart_10px.png",
- fap_category="Misc",
+ fap_category="GPIO",
)
diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c
index eed9e4205..33ec5fd01 100644
--- a/applications/debug/unit_tests/furi/furi_test.c
+++ b/applications/debug/unit_tests/furi/furi_test.c
@@ -5,7 +5,6 @@
// v2 tests
void test_furi_create_open();
-void test_furi_valuemutex();
void test_furi_concurrent_access();
void test_furi_pubsub();
@@ -30,10 +29,6 @@ MU_TEST(mu_test_furi_create_open) {
test_furi_create_open();
}
-MU_TEST(mu_test_furi_valuemutex) {
- test_furi_valuemutex();
-}
-
MU_TEST(mu_test_furi_pubsub) {
test_furi_pubsub();
}
@@ -51,7 +46,6 @@ MU_TEST_SUITE(test_suite) {
// v2 tests
MU_RUN_TEST(mu_test_furi_create_open);
- MU_RUN_TEST(mu_test_furi_valuemutex);
MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr);
}
diff --git a/applications/debug/unit_tests/furi/furi_valuemutex_test.c b/applications/debug/unit_tests/furi/furi_valuemutex_test.c
deleted file mode 100644
index 02fd47eeb..000000000
--- a/applications/debug/unit_tests/furi/furi_valuemutex_test.c
+++ /dev/null
@@ -1,41 +0,0 @@
-#include
-#include
-#include
-
-#include "../minunit.h"
-
-void test_furi_valuemutex() {
- const int init_value = 0xdeadbeef;
- const int changed_value = 0x12345678;
-
- int value = init_value;
- bool result;
- ValueMutex valuemutex;
-
- // init mutex case
- result = init_mutex(&valuemutex, &value, sizeof(value));
- mu_assert(result, "init mutex failed");
-
- // acquire mutex case
- int* value_pointer = acquire_mutex(&valuemutex, 100);
- mu_assert_pointers_eq(value_pointer, &value);
-
- // second acquire mutex case
- int* value_pointer_second = acquire_mutex(&valuemutex, 100);
- mu_assert_pointers_eq(value_pointer_second, NULL);
-
- // change value case
- *value_pointer = changed_value;
- mu_assert_int_eq(value, changed_value);
-
- // release mutex case
- result = release_mutex(&valuemutex, &value);
- mu_assert(result, "release mutex failed");
-
- // TODO
- //acquire mutex blocking case
- //write mutex blocking case
- //read mutex blocking case
-
- mu_check(delete_mutex(&valuemutex));
-}
diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c
index ce2c7aad7..a9b66b221 100644
--- a/applications/debug/unit_tests/power/power_test.c
+++ b/applications/debug/unit_tests/power/power_test.c
@@ -3,56 +3,63 @@
#include "../minunit.h"
static void power_test_deinit(void) {
- // Try to reset to default charging voltage
- furi_hal_power_set_battery_charging_voltage(4.208f);
+ // Try to reset to default charge voltage limit
+ furi_hal_power_set_battery_charge_voltage_limit(4.208f);
}
-MU_TEST(test_power_charge_voltage_exact) {
- // Power of 16mV charge voltages get applied exactly
+MU_TEST(test_power_charge_voltage_limit_exact) {
+ // Power of 16mV charge voltage limits get applied exactly
// (bq25896 charge controller works in 16mV increments)
//
// This test may need adapted if other charge controllers are used in the future.
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
float charge_volt = (float)charge_mv / 1000.0f;
- furi_hal_power_set_battery_charging_voltage(charge_volt);
- mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(charge_volt);
+ mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit());
}
}
-MU_TEST(test_power_charge_voltage_floating_imprecision) {
+MU_TEST(test_power_charge_voltage_limit_floating_imprecision) {
// 4.016f should act as 4.016 V, even with floating point imprecision
- furi_hal_power_set_battery_charging_voltage(4.016f);
- mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.016f);
+ mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit());
}
-MU_TEST(test_power_charge_voltage_inexact) {
- // Charge voltages that are not power of 16mV get truncated down
- furi_hal_power_set_battery_charging_voltage(3.841f);
- mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
+MU_TEST(test_power_charge_voltage_limit_inexact) {
+ // Charge voltage limits that are not power of 16mV get truncated down
+ furi_hal_power_set_battery_charge_voltage_limit(3.841f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
- furi_hal_power_set_battery_charging_voltage(3.900f);
- mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(3.900f);
+ mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit());
- furi_hal_power_set_battery_charging_voltage(4.200f);
- mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.200f);
+ mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit());
}
-MU_TEST(test_power_charge_voltage_invalid_clamped) {
- // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V
- furi_hal_power_set_battery_charging_voltage(3.808f);
- mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
+MU_TEST(test_power_charge_voltage_limit_invalid_clamped) {
+ // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V
+ furi_hal_power_set_battery_charge_voltage_limit(3.808f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
+ furi_hal_power_set_battery_charge_voltage_limit(1.0f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
// unhappy battery if this fails.
- furi_hal_power_set_battery_charging_voltage(4.240f);
- mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.240f);
+ mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
+ // Likewise, picking a number that the uint8_t wraparound in the driver would result in a
+ // VREG value under 23 if this test fails.
+ // E.g. (uint8_t)((8105-3840)/16) -> 10
+ furi_hal_power_set_battery_charge_voltage_limit(8.105f);
+ mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
}
MU_TEST_SUITE(test_power_suite) {
- MU_RUN_TEST(test_power_charge_voltage_exact);
- MU_RUN_TEST(test_power_charge_voltage_floating_imprecision);
- MU_RUN_TEST(test_power_charge_voltage_inexact);
- MU_RUN_TEST(test_power_charge_voltage_invalid_clamped);
+ MU_RUN_TEST(test_power_charge_voltage_limit_exact);
+ MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision);
+ MU_RUN_TEST(test_power_charge_voltage_limit_inexact);
+ MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped);
power_test_deinit();
}
diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c
index 5b52df2fa..329f3b741 100644
--- a/applications/debug/unit_tests/rpc/rpc_test.c
+++ b/applications/debug/unit_tests/rpc/rpc_test.c
@@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
}
furi_check(rpc_session[0].session);
- rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1);
+ rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
@@ -191,7 +191,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) {
size_t size = strlen(clean_dir) + strlen(name) + 1 + 1;
char* fullname = malloc(size);
snprintf(fullname, size, "%s/%s", clean_dir, name);
- if(fileinfo.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&fileinfo)) {
clean_directory(fs_api, fullname);
}
FS_Error error = storage_common_remove(fs_api, fullname);
@@ -608,9 +608,8 @@ static void test_rpc_storage_list_create_expected_list(
}
if(path_contains_only_ascii(name)) {
- list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ?
- PB_Storage_File_FileType_DIR :
- PB_Storage_File_FileType_FILE;
+ list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :
+ PB_Storage_File_FileType_FILE;
list->file[i].size = fileinfo.size;
list->file[i].data = NULL;
/* memory free inside rpc_encode_and_send() -> pb_release() */
@@ -873,7 +872,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
if(error == FSE_OK) {
response->which_content = PB_Main_storage_stat_response_tag;
response->content.storage_stat_response.has_file = true;
- response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
+ response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ?
PB_Storage_File_FileType_DIR :
PB_Storage_File_FileType_FILE;
response->content.storage_stat_response.file.size = fileinfo.size;
diff --git a/applications/debug/unit_tests/storage/dirwalk_test.c b/applications/debug/unit_tests/storage/dirwalk_test.c
index 97aaa3580..e0842a7a4 100644
--- a/applications/debug/unit_tests/storage/dirwalk_test.c
+++ b/applications/debug/unit_tests/storage/dirwalk_test.c
@@ -179,7 +179,7 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
@@ -204,7 +204,7 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
@@ -219,7 +219,7 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein
UNUSED(ctx);
// only files
- if(!(fileinfo->flags & FSF_DIRECTORY)) {
+ if(!file_info_is_dir(fileinfo)) {
// with ".test" in name
if(strstr(name, ".test") != NULL) {
return true;
@@ -243,7 +243,7 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c
index 115009701..582be7902 100644
--- a/applications/debug/unit_tests/storage/storage_test.c
+++ b/applications/debug/unit_tests/storage/storage_test.c
@@ -2,9 +2,40 @@
#include
#include
+// DO NOT USE THIS IN PRODUCTION CODE
+// This is a hack to access internal storage functions and definitions
+#include
+
+#define UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/" path)
+
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
+#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir")
+
+static bool storage_file_create(Storage* storage, const char* path, const char* data) {
+ File* file = storage_file_alloc(storage);
+ bool result = false;
+ do {
+ if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_NEW)) {
+ break;
+ }
+
+ if(storage_file_write(file, data, strlen(data)) != strlen(data)) {
+ break;
+ }
+
+ if(!storage_file_close(file)) {
+ break;
+ }
+
+ result = true;
+ } while(0);
+
+ storage_file_free(file);
+ return result;
+}
+
static void storage_file_open_lock_setup() {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
@@ -115,7 +146,7 @@ static int32_t storage_dir_locker(void* ctx) {
File* file = storage_file_alloc(storage);
furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
furi_semaphore_release(semaphore);
- furi_delay_ms(1000);
+ furi_delay_ms(100);
furi_check(storage_dir_close(file));
furi_record_close(RECORD_STORAGE);
@@ -152,9 +183,21 @@ MU_TEST(storage_dir_open_lock) {
mu_assert(result, "cannot open locked dir");
}
+MU_TEST(storage_dir_exists_test) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ mu_check(!storage_dir_exists(storage, STORAGE_TEST_DIR));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, STORAGE_TEST_DIR));
+ mu_check(storage_dir_exists(storage, STORAGE_TEST_DIR));
+ mu_assert_int_eq(FSE_OK, storage_common_remove(storage, STORAGE_TEST_DIR));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
MU_TEST_SUITE(storage_dir) {
MU_RUN_TEST(storage_dir_open_close);
MU_RUN_TEST(storage_dir_open_lock);
+ MU_RUN_TEST(storage_dir_exists_test);
}
static const char* const storage_copy_test_paths[] = {
@@ -303,9 +346,256 @@ MU_TEST_SUITE(storage_rename) {
furi_record_close(RECORD_STORAGE);
}
+#define APPSDATA_APP_PATH(path) APPS_DATA_PATH "/" path
+
+static const char* storage_test_apps[] = {
+ "-_twilight_-",
+ "-_rainbow_-",
+ "-_pinkie_-",
+ "-_apple_-",
+ "-_flutter_-",
+ "-_rare_-",
+};
+
+static size_t storage_test_apps_count = COUNT_OF(storage_test_apps);
+
+static int32_t storage_test_app(void* arg) {
+ UNUSED(arg);
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_common_remove(storage, "/app/test");
+ int32_t ret = storage_file_create(storage, "/app/test", "test");
+ furi_record_close(RECORD_STORAGE);
+ return ret;
+}
+
+MU_TEST(test_storage_data_path_apps) {
+ for(size_t i = 0; i < storage_test_apps_count; i++) {
+ FuriThread* thread =
+ furi_thread_alloc_ex(storage_test_apps[i], 1024, storage_test_app, NULL);
+ furi_thread_set_appid(thread, storage_test_apps[i]);
+ furi_thread_start(thread);
+ furi_thread_join(thread);
+
+ mu_assert_int_eq(true, furi_thread_get_return_code(thread));
+
+ // Check if app data dir and file exists
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FuriString* expected = furi_string_alloc();
+ furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
+
+ mu_check(storage_dir_exists(storage, furi_string_get_cstr(expected)));
+ furi_string_cat(expected, "/test");
+ mu_check(storage_file_exists(storage, furi_string_get_cstr(expected)));
+
+ furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
+ storage_simply_remove_recursive(storage, furi_string_get_cstr(expected));
+
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_free(expected);
+ furi_thread_free(thread);
+ }
+}
+
+MU_TEST(test_storage_data_path) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ File* file = storage_file_alloc(storage);
+ mu_check(storage_dir_open(file, "/app"));
+ mu_check(storage_dir_close(file));
+ storage_file_free(file);
+
+ // check that appsdata folder exists
+ mu_check(storage_dir_exists(storage, APPS_DATA_PATH));
+
+ // check that cli folder exists
+ mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli")));
+
+ storage_simply_remove(storage, APPSDATA_APP_PATH("cli"));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
+MU_TEST(test_storage_common_migrate) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ // Setup test folders
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from non existing
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ // Test migration from existing folder to non existing
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ // Test migration from existing folder to existing folder
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file11")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file21.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext1.ext")));
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from empty folder to existing file
+ // Expected result: FSE_OK, folder removed, file untouched
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from empty folder to existing folder
+ // Expected result: FSE_OK, old folder removed, new folder untouched
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from existing file to non existing, no extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from existing file to non existing, with extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
+
+ // Test migration from existing file to existing file, no extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test2"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
+
+ // Test migration from existing file to existing file, with extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new.file"), "test2"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1.file")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1.file"));
+
+ // Test migration from existing file to existing folder
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
+MU_TEST_SUITE(test_data_path) {
+ MU_RUN_TEST(test_storage_data_path);
+ MU_RUN_TEST(test_storage_data_path_apps);
+}
+
+MU_TEST_SUITE(test_storage_common) {
+ MU_RUN_TEST(test_storage_common_migrate);
+}
+
int run_minunit_test_storage() {
MU_RUN_SUITE(storage_file);
MU_RUN_SUITE(storage_dir);
MU_RUN_SUITE(storage_rename);
+ MU_RUN_SUITE(test_data_path);
+ MU_RUN_SUITE(test_storage_common);
return MU_EXIT_CODE;
}
diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
index fcd3dbded..c7e9c96f1 100644
--- a/applications/debug/unit_tests/subghz/subghz_test.c
+++ b/applications/debug/unit_tests/subghz/subghz_test.c
@@ -12,8 +12,9 @@
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
#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 ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
-#define TEST_RANDOM_COUNT_PARSE 273
+#define TEST_RANDOM_COUNT_PARSE 329
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
@@ -43,6 +44,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_alutech_at_4n_rainbow_table_file_name(
+ environment_handler, ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_protocol_registry(
environment_handler, (void*)&subghz_protocol_registry);
@@ -489,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
+MU_TEST(subghz_decoder_linear_delta3_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"),
+ SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
+}
+
MU_TEST(subghz_decoder_megacode_test) {
mu_assert(
subghz_decoder_test(
@@ -590,12 +601,6 @@ MU_TEST(subghz_decoder_ansonic_test) {
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
}
-MU_TEST(subghz_decoder_pocsag_test) {
- mu_assert(
- subghz_decoder_test(EXT_PATH("unit_tests/subghz/pocsag.sub"), SUBGHZ_PROTOCOL_POCSAG_NAME),
- "Test decoder " SUBGHZ_PROTOCOL_POCSAG_NAME " error\r\n");
-}
-
MU_TEST(subghz_decoder_smc5326_test) {
mu_assert(
subghz_decoder_test(
@@ -610,6 +615,36 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) {
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
}
+MU_TEST(subghz_decoder_dooya_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_alutech_at_4n_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"),
+ SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_nice_one_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"),
+ SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
+}
+
//test encoders
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
@@ -653,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
+MU_TEST(subghz_encoder_linear_delta3_test) {
+ mu_assert(
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
+}
+
MU_TEST(subghz_encoder_megacode_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
@@ -749,6 +790,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) {
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
}
+MU_TEST(subghz_encoder_dooya_test) {
+ mu_assert(
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
@@ -778,6 +825,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
MU_RUN_TEST(subghz_decoder_star_line_test);
MU_RUN_TEST(subghz_decoder_linear_test);
+ MU_RUN_TEST(subghz_decoder_linear_delta3_test);
MU_RUN_TEST(subghz_decoder_megacode_test);
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
@@ -792,9 +840,12 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_ansonic_test);
- MU_RUN_TEST(subghz_decoder_pocsag_test);
MU_RUN_TEST(subghz_decoder_smc5326_test);
MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
+ MU_RUN_TEST(subghz_decoder_dooya_test);
+ MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
+ MU_RUN_TEST(subghz_decoder_nice_one_test);
+ MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test);
@@ -803,6 +854,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test);
MU_RUN_TEST(subghz_encoder_linear_test);
+ MU_RUN_TEST(subghz_encoder_linear_delta3_test);
MU_RUN_TEST(subghz_encoder_megacode_test);
MU_RUN_TEST(subghz_encoder_holtek_test);
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
@@ -819,6 +871,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_smc5326_test);
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
+ MU_RUN_TEST(subghz_encoder_dooya_test);
MU_RUN_TEST(subghz_random_test);
subghz_test_deinit();
diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c
index 2bb9c423f..ac71ca397 100644
--- a/applications/debug/unit_tests/test_index.c
+++ b/applications/debug/unit_tests/test_index.c
@@ -70,7 +70,7 @@ void minunit_print_progress() {
}
void minunit_print_fail(const char* str) {
- printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
+ printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
}
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md
new file mode 100644
index 000000000..fd8666077
--- /dev/null
+++ b/applications/examples/example_apps_data/README.md
@@ -0,0 +1,18 @@
+# Apps Data folder Example
+
+This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth.
+
+## What is the Apps Data Folder?
+
+The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware.
+
+The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
+The Apps Data folder is located only on the external storage, the SD card.
+
+For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/app` alias instead.
+
+## How to get the path to the Apps Data folder?
+
+You can use `/app` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/app/config.txt`. But this way is not recommended, because even the `/app` alias can change in the future.
+
+We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
\ No newline at end of file
diff --git a/applications/examples/example_apps_data/application.fam b/applications/examples/example_apps_data/application.fam
new file mode 100644
index 000000000..f44dca97d
--- /dev/null
+++ b/applications/examples/example_apps_data/application.fam
@@ -0,0 +1,9 @@
+App(
+ appid="example_apps_data",
+ name="Example: Apps Data",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_apps_data_main",
+ requires=["gui"],
+ stack_size=1 * 1024,
+ fap_category="Examples",
+)
diff --git a/applications/examples/example_apps_data/example_apps_data.c b/applications/examples/example_apps_data/example_apps_data.c
new file mode 100644
index 000000000..d6104c137
--- /dev/null
+++ b/applications/examples/example_apps_data/example_apps_data.c
@@ -0,0 +1,40 @@
+#include
+#include
+
+// Define log tag
+#define TAG "example_apps_data"
+
+// Application entry point
+int32_t example_apps_data_main(void* p) {
+ // Mark argument as unused
+ UNUSED(p);
+
+ // Open storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ // Allocate file
+ File* file = storage_file_alloc(storage);
+
+ // Get the path to the current application data folder
+ // That is: /ext/apps_data/
+ // And it will create folders in the path if they don't exist
+ // In this example it will create /ext/apps_data/example_apps_data
+ // And file will be /ext/apps_data/example_apps_data/test.txt
+
+ // Open file, write data and close it
+ if(!storage_file_open(file, APP_DATA_PATH("test.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+ FURI_LOG_E(TAG, "Failed to open file");
+ }
+ if(!storage_file_write(file, "Hello World!", strlen("Hello World!"))) {
+ FURI_LOG_E(TAG, "Failed to write to file");
+ }
+ storage_file_close(file);
+
+ // Deallocate file
+ storage_file_free(file);
+
+ // Close storage
+ furi_record_close(RECORD_STORAGE);
+
+ return 0;
+}
diff --git a/applications/main/application.fam b/applications/main/application.fam
index eefb801b3..459a878ed 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -3,36 +3,16 @@ App(
name="Basic applications for main menu",
apptype=FlipperAppType.METAPACKAGE,
provides=[
+ "fap_loader",
+ "subghz",
+ "subghz_remote",
+ "lfrfid",
+ "nfc",
+ "infrared",
"gpio",
"ibutton",
- "infrared",
- "lfrfid",
- "nfc",
- "subghz",
"bad_kb",
"u2f",
- "fap_loader",
- "sub_playlist",
- "archive",
- "clock",
- "unirfremix",
- ],
-)
-
-App(
- appid="main_apps_default",
- name="Basic applications for main menu",
- apptype=FlipperAppType.METAPACKAGE,
- provides=[
- "gpio",
- # "ibutton",
- "infrared",
- "lfrfid",
- "nfc",
- "subghz",
- # "bad_kb",
- # "u2f",
- "fap_loader",
"archive",
],
)
diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c
index 78b010a78..f93d9694d 100644
--- a/applications/main/archive/helpers/archive_browser.c
+++ b/applications/main/archive/helpers/archive_browser.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
static void
archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {
@@ -450,7 +451,7 @@ static bool archive_is_dir_exists(FuriString* path) {
FileInfo file_info;
Storage* storage = furi_record_open(RECORD_STORAGE);
if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
- if(file_info.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&file_info)) {
state = true;
}
}
@@ -464,14 +465,17 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
browser->last_tab_switch_dir = key;
- for(int i = 0; i < 2; i++) {
+ if(key == InputKeyLeft) {
+ tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
+ } else {
+ tab = (tab + 1) % ArchiveTabTotal;
+ }
+ if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
if(key == InputKeyLeft) {
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
} else {
tab = (tab + 1) % ArchiveTabTotal;
}
- if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue;
- break;
}
browser->is_root = true;
@@ -528,12 +532,16 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) {
browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);
furi_string_set(browser->path, path);
+
file_browser_worker_folder_enter(browser->worker, path, idx_temp);
}
void archive_leave_dir(ArchiveBrowserView* browser) {
furi_assert(browser);
+ size_t dirname_start = furi_string_search_rchar(browser->path, '/');
+ furi_string_left(browser->path, dirname_start);
+
file_browser_worker_folder_exit(browser->worker);
}
diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c
index 8bbcb5213..f395ee5a1 100644
--- a/applications/main/archive/helpers/archive_favorites.c
+++ b/applications/main/archive/helpers/archive_favorites.c
@@ -160,7 +160,7 @@ bool archive_favorites_read(void* context) {
if(storage_file_exists(storage, furi_string_get_cstr(buffer))) {
storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info);
archive_add_file_item(
- browser, (file_info.flags & FSF_DIRECTORY), furi_string_get_cstr(buffer));
+ browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer));
file_count++;
} else {
need_refresh = true;
diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c
index 83eb2a845..5f5d13fc3 100644
--- a/applications/main/archive/helpers/archive_files.c
+++ b/applications/main/archive/helpers/archive_files.c
@@ -96,7 +96,7 @@ void archive_delete_file(void* context, const char* format, ...) {
bool res = false;
- if(fileinfo.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&fileinfo)) {
res = storage_simply_remove_recursive(fs_api, furi_string_get_cstr(filename));
} else {
res = (storage_common_remove(fs_api, furi_string_get_cstr(filename)) == FSE_OK);
diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h
index 863a8e7da..431c701b3 100644
--- a/applications/main/archive/helpers/archive_files.h
+++ b/applications/main/archive/helpers/archive_files.h
@@ -84,7 +84,7 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
}
static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
- if(!XTREME_SETTINGS()->sort_ignore_dirs) {
+ if(XTREME_SETTINGS()->sort_dirs_first) {
if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
return -1;
}
diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c
index dfce5acbf..9f2a3145a 100644
--- a/applications/main/bad_kb/bad_kb_app.c
+++ b/applications/main/bad_kb/bad_kb_app.c
@@ -37,9 +37,26 @@ static void bad_kb_load_settings(BadKbApp* app) {
!storage_file_eof(settings_file) && !isspace(chr)) {
furi_string_push_back(app->keyboard_layout, chr);
}
+ } else {
+ furi_string_reset(app->keyboard_layout);
}
storage_file_close(settings_file);
storage_file_free(settings_file);
+
+ if(!furi_string_empty(app->keyboard_layout)) {
+ Storage* fs_api = furi_record_open(RECORD_STORAGE);
+ FileInfo layout_file_info;
+ FS_Error file_check_err = storage_common_stat(
+ fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
+ furi_record_close(RECORD_STORAGE);
+ if(file_check_err != FSE_OK) {
+ furi_string_reset(app->keyboard_layout);
+ return;
+ }
+ if(layout_file_info.size != 256) {
+ furi_string_reset(app->keyboard_layout);
+ }
+ }
}
static void bad_kb_save_settings(BadKbApp* app) {
@@ -55,17 +72,6 @@ static void bad_kb_save_settings(BadKbApp* app) {
storage_file_free(settings_file);
}
-void bad_kb_set_name(BadKbApp* app, const char* fmt, ...) {
- furi_assert(app);
-
- va_list args;
- va_start(args, fmt);
-
- vsnprintf(app->name, BAD_KB_ADV_NAME_MAX_LEN, fmt, args);
-
- va_end(args);
-}
-
BadKbApp* bad_kb_app_alloc(char* arg) {
BadKbApp* app = malloc(sizeof(BadKbApp));
@@ -77,6 +83,18 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
furi_string_set(app->file_path, arg);
}
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ // Remove old pre-included files to avoid duplicates on migrate
+ storage_simply_remove(storage, EXT_PATH("badusb/layouts"));
+ storage_simply_remove(storage, EXT_PATH("badusb/.badusb.settings"));
+ storage_simply_remove(storage, EXT_PATH("badusb/Kiosk-Evasion-Bruteforce.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/Wifi-Stealer_ORG.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/demo_macos.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/demo_windows.txt"));
+ storage_common_migrate(storage, EXT_PATH("badusb"), BAD_KB_APP_BASE_FOLDER);
+ storage_simply_mkdir(storage, BAD_KB_APP_BASE_FOLDER);
+ furi_record_close(RECORD_STORAGE);
+
bad_kb_load_settings(app);
app->gui = furi_record_open(RECORD_GUI);
@@ -96,14 +114,19 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bad_kb_app_back_event_callback);
+ app->connection_init = false;
+
Bt* bt = furi_record_open(RECORD_BT);
app->bt = bt;
+ app->bt->suppress_pin_screen = true;
app->is_bt = XTREME_SETTINGS()->bad_bt;
- const char* adv_name = bt_get_profile_adv_name(bt);
+ app->bt_remember = XTREME_SETTINGS()->bad_bt_remember;
+ const char* adv_name = furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard);
memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
- const uint8_t* mac_addr = bt_get_profile_mac_address(bt);
+ // need to be done before bt init (where mac address get modified if bounding is activated)
+ const uint8_t* mac_addr = furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard);
memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
@@ -139,8 +162,12 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
if(furi_hal_usb_is_locked()) {
app->error = BadKbAppErrorCloseRpc;
+ app->conn_init_thread = NULL;
scene_manager_next_scene(app->scene_manager, BadKbSceneError);
} else {
+ app->conn_init_thread = furi_thread_alloc_ex(
+ "BadKbConnInit", 512, (FuriThreadCallback)bad_kb_connection_init, app);
+ furi_thread_start(app->conn_init_thread);
if(!furi_string_empty(app->file_path)) {
app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
@@ -188,16 +215,16 @@ void bad_kb_app_free(BadKbApp* app) {
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
- // restores bt config
- // BtProfile have already been switched to the previous one
- // so we directly modify the right profile
- bad_kb_connection_deinit(app->bt);
+ // Restore bt config
+ // BtProfile has already been switched to the previous one
+ // So we directly modify the right profile
if(strcmp(app->bt_old_config.name, app->name) != 0) {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name);
}
if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) {
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac);
}
+ app->bt->suppress_pin_screen = false;
// Close records
furi_record_close(RECORD_GUI);
@@ -210,6 +237,12 @@ void bad_kb_app_free(BadKbApp* app) {
furi_string_free(app->file_path);
furi_string_free(app->keyboard_layout);
+ if(app->conn_init_thread) {
+ furi_thread_join(app->conn_init_thread);
+ furi_thread_free(app->conn_init_thread);
+ }
+ bad_kb_connection_deinit(app);
+
free(app);
}
diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h
index e75a94651..af64be253 100644
--- a/applications/main/bad_kb/bad_kb_app.h
+++ b/applications/main/bad_kb/bad_kb_app.h
@@ -6,8 +6,6 @@ extern "C" {
typedef struct BadKbApp BadKbApp;
-void bad_kb_set_name(BadKbApp* app, const char* fmt, ...);
-
#ifdef __cplusplus
}
#endif
diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h
index 913830e72..a55875676 100644
--- a/applications/main/bad_kb/bad_kb_app_i.h
+++ b/applications/main/bad_kb/bad_kb_app_i.h
@@ -16,14 +16,19 @@
#include
#include "views/bad_kb_view.h"
-#define BAD_KB_APP_BASE_FOLDER ANY_PATH("badkb")
-#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/layouts"
+#define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb")
+#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts"
+#define BAD_KB_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badkb/.bt_keys")
+#define BAD_KB_APP_PATH_BOUND_KEYS_FILE BAD_KB_APP_PATH_BOUND_KEYS_FOLDER "/.devices.keys"
#define BAD_KB_APP_SCRIPT_EXTENSION ".txt"
#define BAD_KB_APP_LAYOUT_EXTENSION ".kl"
#define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
#define BAD_KB_ADV_NAME_MAX_LEN 18
+// this is the MAC address used when we do not forget paired device (BOUND STATE)
+#define BAD_KB_BOUND_MAC_ADDRESS {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4};
+
typedef enum {
BadKbAppErrorNoFiles,
BadKbAppErrorCloseRpc,
@@ -36,12 +41,9 @@ typedef enum BadKbCustomEvent {
} BadKbCustomEvent;
typedef struct {
+ //uint8_t bounded_mac[BAD_KB_MAC_ADDRESS_LEN];
uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
-
- // number of bt keys before starting the app (all keys added in
- // the bt keys file then will be removed)
- uint16_t n_keys;
} BadKbBtConfig;
struct BadKbApp {
@@ -59,6 +61,7 @@ struct BadKbApp {
ByteInput* byte_input;
uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
+ bool bt_remember; // weither we remember paired devices or not
BadKbBtConfig bt_old_config;
BadKbAppError error;
@@ -68,6 +71,12 @@ struct BadKbApp {
BadKbScript* bad_kb_script;
bool is_bt;
+
+ bool connection_init;
+ FuriHalUsbInterface* usb_prev_mode;
+ GapPairing bt_prev_mode;
+
+ FuriThread* conn_init_thread;
};
typedef enum {
diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c
index e2fd464ff..b91f36b9f 100644
--- a/applications/main/bad_kb/bad_kb_script.c
+++ b/applications/main/bad_kb/bad_kb_script.c
@@ -11,6 +11,8 @@
#include
+#include "bad_kb_app_i.h"
+
#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
#define TAG "BadKB"
@@ -41,12 +43,6 @@ typedef enum {
LevelRssiError = 0xFF,
} LevelRssiRange;
-typedef enum {
- BadKbConnectionModeNone,
- BadKbConnectionModeUsb,
- BadKbConnectionModeBt,
-} BadKbConnectionMode;
-
/**
* Delays for waiting between HID key press and key release
*/
@@ -65,6 +61,7 @@ struct BadKbScript {
FuriString* keyboard_layout;
uint32_t defdelay;
uint16_t layout[128];
+ uint32_t stringdelay;
FuriThread* thread;
uint8_t file_buf[FILE_BUFFER_LEN + 1];
uint8_t buf_start;
@@ -148,6 +145,8 @@ static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "};
static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "};
static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "};
+static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "};
+static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "};
static const char ducky_cmd_repeat[] = {"REPEAT "};
static const char ducky_cmd_sysrq[] = {"SYSRQ "};
@@ -170,27 +169,22 @@ static const uint8_t numpad_keys[10] = {
HID_KEYPAD_9,
};
-BadKbConnectionMode connection_mode = BadKbConnectionModeNone;
-FuriHalUsbInterface* usb_mode_prev = NULL;
-GapPairing bt_mode_prev = GapPairingNone;
-bool bt_connected = false;
-bool usb_connected = false;
uint8_t bt_timeout = 0;
static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
- BtRssi rssi_data = {0};
+ uint8_t rssi;
- if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError;
+ if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError;
- if(rssi_data.rssi <= 39)
+ if(rssi <= 39)
return LevelRssi39_0;
- else if(rssi_data.rssi <= 59)
+ else if(rssi <= 59)
return LevelRssi59_40;
- else if(rssi_data.rssi <= 79)
+ else if(rssi <= 79)
return LevelRssi79_60;
- else if(rssi_data.rssi <= 99)
+ else if(rssi <= 99)
return LevelRssi99_80;
- else if(rssi_data.rssi <= 122)
+ else if(rssi <= 122)
return LevelRssi122_100;
return LevelRssiError;
@@ -304,6 +298,7 @@ static bool ducky_altstring(BadKbScript* bad_kb, const char* param) {
static bool ducky_string(BadKbScript* bad_kb, const char* param) {
uint32_t i = 0;
+
while(param[i] != '\0') {
uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]);
if(keycode != HID_KEYBOARD_NONE) {
@@ -315,9 +310,13 @@ static bool ducky_string(BadKbScript* bad_kb, const char* param) {
furi_hal_hid_kb_press(keycode);
furi_hal_hid_kb_release(keycode);
}
+ if(bad_kb->stringdelay > 0) {
+ furi_delay_ms(bad_kb->stringdelay);
+ }
}
i++;
}
+ bad_kb->stringdelay = 0;
return true;
}
@@ -379,6 +378,19 @@ static int32_t
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(
+ (strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) ||
+ (strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) {
+ //STRINGDELAY, finally it's here
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ state = ducky_get_number(line_tmp, &bad_kb->stringdelay);
+ if((state) && (bad_kb->stringdelay > 0)) {
+ return state;
+ }
+ if(error != NULL) {
+ snprintf(error, error_len, "Invalid number %s", line_tmp);
+ }
+ return SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
// STRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
@@ -607,10 +619,8 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) {
if(r != LevelRssiError) {
bt_timeout = bt_hid_delays[r];
}
- bt_connected = true;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
} else {
- bt_connected = false;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
}
}
@@ -620,83 +630,116 @@ static void bad_kb_usb_hid_state_callback(bool state, void* context) {
BadKbScript* bad_kb = context;
if(state == true) {
- usb_connected = true;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
} else {
- usb_connected = false;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
}
}
-void bad_kb_bt_init(Bt* bt) {
- bt_timeout = bt_hid_delays[LevelRssi39_0];
- bt_disconnect(bt);
- furi_delay_ms(200);
- bt_keys_storage_set_storage_path(bt, HID_BT_KEYS_STORAGE_PATH);
- furi_assert(bt_set_profile(bt, BtProfileHidKeyboard));
- bt_mode_prev = bt_get_profile_pairing_method(bt);
- bt_set_profile_pairing_method(bt, GapPairingNone);
- furi_hal_bt_start_advertising();
- // disable peer key adding to bt SRAM storage
- bt_disable_peer_key_update(bt);
-
- connection_mode = BadKbConnectionModeBt;
+void bad_kb_reload_worker(BadKbApp* app) {
+ bad_kb_script_close(app->bad_kb_script);
+ app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
+ bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
}
-void bad_kb_bt_deinit(Bt* bt) {
- // release all keys
- // bt_hid_hold_while_keyboard_buffer_full(6, 3000);
+void bad_kb_config_switch_mode(BadKbApp* app) {
+ scene_manager_previous_scene(app->scene_manager);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
+ } else {
+ furi_hal_bt_stop_advertising();
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ }
+ bad_kb_reload_worker(app);
+}
- // stop ble
- bt_disconnect(bt);
+void bad_kb_config_switch_remember_mode(BadKbApp* app) {
+ if(app->bt_remember) {
+ // set bouding mac
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ bt_set_profile_mac_address(app->bt, mac); // this also restart bt
+ // enable keys storage
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ // set back user defined mac address
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ bt_set_profile_mac_address(app->bt, app->mac);
+ // disable key storage
+ bt_disable_peer_key_update(app->bt);
+ }
+ bad_kb_reload_worker(app);
+}
- // Wait 2nd core to update nvm storage
- furi_delay_ms(200);
+int32_t bad_kb_connection_init(BadKbApp* app) {
+ app->usb_prev_mode = furi_hal_usb_get_config();
+ furi_hal_usb_set_config(NULL, NULL);
- bt_keys_storage_set_default_path(bt);
+ bt_timeout = bt_hid_delays[LevelRssi39_0];
+ bt_disconnect(app->bt);
+ // furi_delay_ms(200);
+ bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE);
+ app->bt_prev_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard);
+ if(app->bt_remember) {
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
+ // using GapPairingNone breaks bounding between devices
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ } else {
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ }
- bt_set_profile_pairing_method(bt, bt_mode_prev);
+ bt_set_profile(app->bt, BtProfileHidKeyboard);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ if(app->bt_remember) {
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ bt_disable_peer_key_update(app->bt); // disable peer key adding to bt SRAM storage
+ }
+ } else {
+ furi_hal_bt_stop_advertising();
+ }
+ app->connection_init = true;
+
+ return 0;
+}
+
+void bad_kb_connection_deinit(BadKbApp* app) {
+ if(!app->connection_init) return;
+
+ furi_hal_usb_set_config(app->usb_prev_mode, NULL);
+
+ // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // release all keys
+ bt_disconnect(app->bt); // stop ble
+ // furi_delay_ms(200); // Wait 2nd core to update nvm storage
+ bt_keys_storage_set_default_path(app->bt);
+ if(app->bt_remember) {
+ // hal primitives doesn't restarts ble, that's what we want cuz we are shutting down
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->mac);
+ }
+ bt_enable_peer_key_update(app->bt); // starts saving peer keys (bounded devices)
// fails if ble radio stack isn't ready when switching profile
// if it happens, maybe we should increase the delay after bt_disconnect
- bt_set_profile(bt, BtProfileSerial);
-
- // starts saving peer keys (bounded devices)
- bt_enable_peer_key_update(bt);
-
- connection_mode = BadKbConnectionModeNone;
+ bt_set_profile(app->bt, BtProfileSerial);
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->bt_prev_mode);
}
-void bad_kb_usb_init() {
- usb_mode_prev = furi_hal_usb_get_config();
-
- connection_mode = BadKbConnectionModeUsb;
-}
-
-void bad_kb_usb_deinit() {
- furi_hal_usb_set_config(usb_mode_prev, NULL);
-
- connection_mode = BadKbConnectionModeNone;
-}
-
-void bad_kb_connection_init(Bt* bt) {
- if(connection_mode != BadKbConnectionModeNone) return;
-
- if(bt) {
- bad_kb_bt_init(bt);
+static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) {
+ uint32_t flags = furi_thread_flags_get();
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags == 0) {
+ flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);
+ furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));
} else {
- bad_kb_usb_init();
- }
-}
-
-void bad_kb_connection_deinit(Bt* bt) {
- if(connection_mode == BadKbConnectionModeNone) return;
-
- if(connection_mode == BadKbConnectionModeBt) {
- bad_kb_bt_deinit(bt);
- } else {
- bad_kb_usb_deinit();
+ uint32_t state = furi_thread_flags_clear(flags);
+ furi_check((state & FuriFlagError) == 0);
}
+ return flags;
}
static int32_t bad_kb_worker(void* context) {
@@ -705,8 +748,6 @@ static int32_t bad_kb_worker(void* context) {
BadKbWorkerState worker_state = BadKbStateInit;
int32_t delay_val = 0;
- bad_kb_connection_init(bad_kb->bt);
-
if(bad_kb->bt) {
bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb);
} else {
@@ -727,7 +768,11 @@ static int32_t bad_kb_worker(void* context) {
FSOM_OPEN_EXISTING)) {
if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) {
if(bad_kb->bt) {
- worker_state = BadKbStateNotConnected; // Ready to run
+ if(furi_hal_bt_is_connected()) {
+ worker_state = BadKbStateIdle; // Ready to run
+ } else {
+ worker_state = BadKbStateNotConnected; // Not connected
+ }
} else {
if(furi_hal_hid_is_connected()) {
worker_state = BadKbStateIdle; // Ready to run
@@ -745,30 +790,20 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateNotConnected) { // State: Not connected
- if((bad_kb->bt && bt_connected) || (!bad_kb->bt && usb_connected)) {
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtConnect) {
worker_state = BadKbStateIdle; // Ready to run
- } else {
- 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) {
- worker_state = BadKbStateIdle; // Ready to run
- } else if(flags & WorkerEvtToggle) {
- worker_state = BadKbStateWillRun; // Will run when connected
- }
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateWillRun; // Will run when connected
}
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateIdle) { // State: ready to start
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtToggle) { // Start executing script
@@ -777,6 +812,7 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->buf_len = 0;
bad_kb->st.line_cur = 0;
bad_kb->defdelay = 0;
+ bad_kb->stringdelay = 0;
bad_kb->repeat_cnt = 0;
bad_kb->file_end = false;
storage_file_seek(script_file, 0, true);
@@ -788,11 +824,8 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateWillRun) { // State: start on connection
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) { // Start executing script
@@ -801,16 +834,26 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->buf_len = 0;
bad_kb->st.line_cur = 0;
bad_kb->defdelay = 0;
+ bad_kb->stringdelay = 0;
bad_kb->repeat_cnt = 0;
bad_kb->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);
+ flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle,
+ FuriFlagWaitAny | FuriFlagNoClear,
+ 1500);
+ if(flags == (unsigned)FuriFlagErrorTimeout) {
+ // If nothing happened - start script execution
+ worker_state = BadKbStateRunning;
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateIdle;
+ furi_thread_flags_clear(WorkerEvtToggle);
+ }
if(bad_kb->bt) {
update_bt_timeout(bad_kb->bt);
}
bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout);
- worker_state = BadKbStateRunning;
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
worker_state = BadKbStateNotConnected;
}
@@ -875,9 +918,8 @@ static int32_t bad_kb_worker(void* context) {
} else if(
(worker_state == BadKbStateFileError) ||
(worker_state == BadKbStateScriptError)) { // State: error
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags =
+ bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
if(flags & WorkerEvtEnd) {
break;
}
@@ -921,6 +963,7 @@ BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) {
bad_kb->st.state = BadKbStateInit;
bad_kb->st.error[0] = '\0';
+ bad_kb->st.is_bt = !!bt;
bad_kb->bt = bt;
@@ -949,7 +992,7 @@ void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_p
}
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- if(!furi_string_empty(layout_path)) {
+ if(!furi_string_empty(layout_path)) { //-V1051
furi_string_set(bad_kb->keyboard_layout, layout_path);
if(storage_file_open(
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h
index 0ea701eb8..a724971ec 100644
--- a/applications/main/bad_kb/bad_kb_script.h
+++ b/applications/main/bad_kb/bad_kb_script.h
@@ -7,6 +7,8 @@ extern "C" {
#include
#include
+typedef struct BadKbApp BadKbApp;
+
typedef struct BadKbScript BadKbScript;
typedef enum {
@@ -23,6 +25,8 @@ typedef enum {
typedef struct {
BadKbWorkerState state;
+ bool is_bt;
+ uint32_t pin;
uint16_t line_cur;
uint16_t line_nb;
uint32_t delay_remain;
@@ -30,9 +34,13 @@ typedef struct {
char error[64];
} BadKbState;
-void bad_kb_connection_init(Bt* bt);
+void bad_kb_config_switch_mode(BadKbApp* app);
-void bad_kb_connection_deinit(Bt* bt);
+void bad_kb_config_switch_remember_mode(BadKbApp* app);
+
+int32_t bad_kb_connection_init(BadKbApp* app);
+
+void bad_kb_connection_deinit(BadKbApp* app);
BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt);
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
index 4412f0796..70dde9e16 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
@@ -5,6 +5,7 @@
enum VarItemListIndex {
VarItemListIndexConnection,
+ VarItemListIndexRemember,
VarItemListIndexKeyboardLayout,
VarItemListIndexAdvertisementName,
VarItemListIndexMacAddress,
@@ -19,6 +20,15 @@ void bad_kb_scene_config_bt_connection_callback(VariableItem* item) {
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection);
}
+void bad_kb_scene_config_bt_bounding_callback(VariableItem* item) {
+ BadKbApp* bad_kb = variable_item_get_context(item);
+ bad_kb->bt_remember = variable_item_get_current_value_index(item);
+ XTREME_SETTINGS()->bad_bt_remember = bad_kb->bt_remember;
+ XTREME_SETTINGS_SAVE();
+ variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexRemember);
+}
+
void bad_kb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) {
BadKbApp* bad_kb = context;
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index);
@@ -34,11 +44,19 @@ void bad_kb_scene_config_bt_on_enter(void* context) {
variable_item_set_current_value_index(item, bad_kb->is_bt);
variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+ item = variable_item_list_add(
+ var_item_list, "Remember", 2, bad_kb_scene_config_bt_bounding_callback, bad_kb);
+ variable_item_set_current_value_index(item, bad_kb->bt_remember);
+ variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
+
item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb);
item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb);
- item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb);
+ // this doesn't update instantly when toggling between Bounding modes
+ if(!bad_kb->bt_remember) {
+ item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb);
+ }
variable_item_list_set_enter_callback(
var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb);
@@ -56,23 +74,15 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) {
if(event.event == VarItemListIndexKeyboardLayout) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
} else if(event.event == VarItemListIndexConnection) {
- bad_kb_script_close(bad_kb->bad_kb_script);
- bad_kb_connection_deinit(bad_kb->bt);
- bad_kb->bad_kb_script =
- bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
- bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+ bad_kb_config_switch_mode(bad_kb);
+ } else if(event.event == VarItemListIndexRemember) {
+ bad_kb_config_switch_remember_mode(bad_kb);
scene_manager_previous_scene(bad_kb->scene_manager);
- if(bad_kb->is_bt) {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
- }
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
} else if(event.event == VarItemListIndexAdvertisementName) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName);
} else if(event.event == VarItemListIndexMacAddress) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac);
- // } else {
- // furi_crash("Unknown key type");
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
index 3842c59fa..a73822fcc 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
@@ -3,8 +3,6 @@
#include "furi_hal_usb.h"
#include
-#define KEYBOARD_FOLDER "/ext/badkb/layouts"
-
static bool bad_kb_layout_select(BadKbApp* bad_kb) {
furi_assert(bad_kb);
@@ -19,7 +17,8 @@ static bool bad_kb_layout_select(BadKbApp* bad_kb) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
- browser_options.base_path = KEYBOARD_FOLDER;
+ browser_options.base_path = BAD_KB_APP_PATH_LAYOUT_FOLDER;
+ browser_options.skip_assets = false;
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
index 0dc4be10a..d7027537a 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
@@ -2,14 +2,14 @@
#define TAG "BadKbConfigMac"
-static uint8_t* reverse_mac_addr(uint8_t* mac) {
+static uint8_t* reverse_mac_addr(uint8_t* mac, uint8_t* out) {
uint8_t tmp;
for(int i = 0; i < 3; i++) {
tmp = mac[i];
- mac[i] = mac[5 - i];
- mac[5 - i] = tmp;
+ out[i] = mac[5 - i];
+ out[5 - i] = tmp;
}
- return mac;
+ return out;
}
void bad_kb_scene_config_mac_byte_input_callback(void* context) {
@@ -23,13 +23,13 @@ void bad_kb_scene_config_mac_on_enter(void* context) {
// Setup view
ByteInput* byte_input = bad_kb->byte_input;
- byte_input_set_header_text(byte_input, "Enter new MAC address");
+ byte_input_set_header_text(byte_input, "Set BT MAC address");
byte_input_set_result_callback(
byte_input,
bad_kb_scene_config_mac_byte_input_callback,
NULL,
bad_kb,
- reverse_mac_addr(bad_kb->mac),
+ reverse_mac_addr(bad_kb->mac, bad_kb->mac),
GAP_MAC_ADDR_SIZE);
view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac);
}
@@ -40,7 +40,8 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadKbAppCustomEventByteInputDone) {
- bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac));
+ uint8_t mac[GAP_MAC_ADDR_SIZE];
+ bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac, mac));
scene_manager_previous_scene(bad_kb->scene_manager);
consumed = true;
}
@@ -54,4 +55,7 @@ void bad_kb_scene_config_mac_on_exit(void* context) {
// Clear view
byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(bad_kb->byte_input, "");
+
+ // reverse back addr (in case it didn't get modified)
+ reverse_mac_addr(bad_kb->mac, bad_kb->mac);
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
index 82dd7a850..59574c334 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
@@ -10,7 +10,7 @@ void bad_kb_scene_config_name_on_enter(void* context) {
BadKbApp* bad_kb = context;
TextInput* text_input = bad_kb->text_input;
- text_input_set_header_text(text_input, "Set BLE adv name");
+ text_input_set_header_text(text_input, "Set BT device name");
text_input_set_result_callback(
text_input,
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
index 232ef8796..d3ff35fc8 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
@@ -50,19 +50,7 @@ bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) {
if(event.event == VarItemListIndexKeyboardLayout) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
} else if(event.event == VarItemListIndexConnection) {
- bad_kb_script_close(bad_kb->bad_kb_script);
- bad_kb_connection_deinit(bad_kb->bt);
- bad_kb->bad_kb_script =
- bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
- bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
- scene_manager_previous_scene(bad_kb->scene_manager);
- if(bad_kb->is_bt) {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
- }
- // } else {
- // furi_crash("Unknown key type");
+ bad_kb_config_switch_mode(bad_kb);
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
index bb2ddf6ca..ba238f390 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
@@ -22,7 +22,6 @@ static bool bad_kb_file_select(BadKbApp* bad_kb) {
void bad_kb_scene_file_select_on_enter(void* context) {
BadKbApp* bad_kb = context;
- furi_hal_usb_disable();
if(bad_kb->bad_kb_script) {
bad_kb_script_close(bad_kb->bad_kb_script);
bad_kb->bad_kb_script = NULL;
@@ -35,7 +34,6 @@ void bad_kb_scene_file_select_on_enter(void* context) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork);
} else {
- furi_hal_usb_enable();
view_dispatcher_stop(bad_kb->view_dispatcher);
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
index 165ee0289..e962f9132 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_work.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
@@ -16,10 +16,12 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InputKeyLeft) {
- if(app->is_bt) {
- scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ if(bad_kb_is_idle_state(app->bad_kb_view)) {
+ if(app->is_bt) {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
+ } else {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ }
}
consumed = true;
} else if(event.event == InputKeyOk) {
diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c
index 1ffe58e9c..26ec90809 100644
--- a/applications/main/bad_kb/views/bad_kb_view.c
+++ b/applications/main/bad_kb/views/bad_kb_view.c
@@ -1,5 +1,6 @@
#include "bad_kb_view.h"
#include "../bad_kb_script.h"
+#include "../bad_kb_app_i.h"
#include
#include
#include
@@ -24,7 +25,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
BadKbModel* model = _model;
FuriString* disp_str;
- disp_str = furi_string_alloc_set(model->file_name);
+ disp_str = furi_string_alloc_set(model->state.is_bt ? "(BT) " : "(USB) ");
+ furi_string_cat_str(disp_str, model->file_name);
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
@@ -38,6 +40,9 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
furi_string_push_back(disp_str, model->layout[i]);
furi_string_push_back(disp_str, ')');
}
+ if(model->state.pin) {
+ furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin);
+ }
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_draw_str(
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
@@ -51,19 +56,15 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
if(XTREME_ASSETS()->is_nsfw) {
elements_button_center(canvas, "Cum");
} else {
- elements_button_center(canvas, "Start");
+ elements_button_center(canvas, "Run");
}
+ elements_button_left(canvas, "Config");
} else if((model->state.state == BadKbStateRunning) || (model->state.state == BadKbStateDelay)) {
elements_button_center(canvas, "Stop");
} else if(model->state.state == BadKbStateWillRun) {
elements_button_center(canvas, "Cancel");
}
- if((model->state.state == BadKbStateNotConnected) || (model->state.state == BadKbStateIdle) ||
- (model->state.state == BadKbStateDone)) {
- elements_button_left(canvas, "Config");
- }
-
if(model->state.state == BadKbStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
@@ -214,6 +215,14 @@ void bad_kb_set_layout(BadKb* bad_kb, const char* layout) {
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
furi_assert(st);
+ uint32_t pin = 0;
+ if(bad_kb->context != NULL) {
+ BadKbApp* app = bad_kb->context;
+ if(app->bt != NULL) {
+ pin = app->bt->pin;
+ }
+ }
+ st->pin = pin;
with_view_model(
bad_kb->view,
BadKbModel * model,
@@ -223,3 +232,18 @@ void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
},
true);
}
+
+bool bad_kb_is_idle_state(BadKb* bad_kb) {
+ bool is_idle = false;
+ with_view_model(
+ bad_kb->view,
+ BadKbModel * model,
+ {
+ if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) ||
+ (model->state.state == BadKbStateNotConnected)) {
+ is_idle = true;
+ }
+ },
+ false);
+ return is_idle;
+}
diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h
index 24fdf4792..f16814bb4 100644
--- a/applications/main/bad_kb/views/bad_kb_view.h
+++ b/applications/main/bad_kb/views/bad_kb_view.h
@@ -19,3 +19,5 @@ void bad_kb_set_file_name(BadKb* bad_kb, const char* name);
void bad_kb_set_layout(BadKb* bad_kb, const char* layout);
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st);
+
+bool bad_kb_is_idle_state(BadKb* bad_kb);
diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c
index 7911aa068..559463988 100644
--- a/applications/main/fap_loader/fap_loader_app.c
+++ b/applications/main/fap_loader/fap_loader_app.c
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include "elf_cpp/elf_hashtable.h"
#include "fap_loader_app.h"
@@ -35,7 +36,7 @@ bool fap_loader_load_name_and_icon(
if(preload_res == FlipperApplicationPreloadStatusSuccess) {
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
- if(manifest->has_icon) {
+ if(manifest->has_icon && icon_ptr != NULL) {
memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE);
}
furi_string_set(item_name, manifest->name);
@@ -105,6 +106,12 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
FURI_LOG_I(TAG, "FAP Loader is starting app");
FuriThread* thread = flipper_application_spawn(loader->app, NULL);
+
+ FuriString* app_name = furi_string_alloc();
+ path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name);
+ furi_thread_set_appid(thread, furi_string_get_cstr(app_name));
+ furi_string_free(app_name);
+
furi_thread_start(thread);
furi_thread_join(thread);
@@ -146,6 +153,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
.skip_assets = true,
.icon = &I_unknown_10px,
.hide_ext = true,
+ .hide_dot_files = true,
.item_loader_callback = fap_loader_item_callback,
.item_loader_context = loader,
.base_path = EXT_PATH("apps"),
diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c
index 1c1a665c7..2d8f7adbc 100644
--- a/applications/main/gpio/scenes/gpio_scene_start.c
+++ b/applications/main/gpio/scenes/gpio_scene_start.c
@@ -107,6 +107,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} 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/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c
index 85212f42b..79999adb2 100644
--- a/applications/main/ibutton/ibutton.c
+++ b/applications/main/ibutton/ibutton.c
@@ -1,10 +1,6 @@
-#include "ibutton.h"
-#include "assets_icons.h"
#include "ibutton_i.h"
-#include "ibutton/scenes/ibutton_scene.h"
+
#include
-#include
-#include
#include
#define TAG "iButtonApp"
@@ -34,50 +30,13 @@ static const NotificationSequence* ibutton_notification_sequences[] = {
};
static void ibutton_make_app_folder(iButton* ibutton) {
- if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder");
}
-}
-bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog) {
- FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
- bool result = false;
- FuriString* data;
- data = furi_string_alloc();
-
- do {
- if(!flipper_format_file_open_existing(file, furi_string_get_cstr(key_path))) break;
-
- // header
- uint32_t version;
- if(!flipper_format_read_header(file, data, &version)) break;
- if(furi_string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break;
- if(version != 1) break;
-
- // key type
- iButtonKeyType type;
- if(!flipper_format_read_string(file, "Key type", data)) break;
- if(!ibutton_key_get_type_by_string(furi_string_get_cstr(data), &type)) break;
-
- // key data
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
- if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
- break;
-
- ibutton_key_set_type(ibutton->key, type);
- ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE);
-
- result = true;
- } while(false);
-
- flipper_format_free(file);
- furi_string_free(data);
-
- if((!result) && (show_dialog)) {
- dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
- }
-
- return result;
+ furi_record_close(RECORD_STORAGE);
}
static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
@@ -87,14 +46,14 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context)
if(event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event(
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
- rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
- ibutton->rpc_ctx = NULL;
+ rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
+ ibutton->rpc = NULL;
} else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
} else if(event == RpcAppEventLoadFile) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
} else {
- rpc_system_app_confirm(ibutton->rpc_ctx, event, false);
+ rpc_system_app_confirm(ibutton->rpc, event, false);
}
}
@@ -135,13 +94,13 @@ iButton* ibutton_alloc() {
ibutton->gui = furi_record_open(RECORD_GUI);
- ibutton->storage = furi_record_open(RECORD_STORAGE);
ibutton->dialogs = furi_record_open(RECORD_DIALOGS);
ibutton->notifications = furi_record_open(RECORD_NOTIFICATION);
- ibutton->key = ibutton_key_alloc();
- ibutton->key_worker = ibutton_worker_alloc();
- ibutton_worker_start_thread(ibutton->key_worker);
+ ibutton->protocols = ibutton_protocols_alloc();
+ ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols));
+ ibutton->worker = ibutton_worker_alloc(ibutton->protocols);
+ ibutton_worker_start_thread(ibutton->worker);
ibutton->submenu = submenu_alloc();
view_dispatcher_add_view(
@@ -163,9 +122,9 @@ iButton* ibutton_alloc() {
view_dispatcher_add_view(
ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget));
- ibutton->dialog_ex = dialog_ex_alloc();
+ ibutton->loading = loading_alloc();
view_dispatcher_add_view(
- ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex));
+ ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading));
return ibutton;
}
@@ -173,8 +132,8 @@ iButton* ibutton_alloc() {
void ibutton_free(iButton* ibutton) {
furi_assert(ibutton);
- view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx);
- dialog_ex_free(ibutton->dialog_ex);
+ view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading);
+ loading_free(ibutton->loading);
view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget);
widget_free(ibutton->widget);
@@ -194,9 +153,6 @@ void ibutton_free(iButton* ibutton) {
view_dispatcher_free(ibutton->view_dispatcher);
scene_manager_free(ibutton->scene_manager);
- furi_record_close(RECORD_STORAGE);
- ibutton->storage = NULL;
-
furi_record_close(RECORD_NOTIFICATION);
ibutton->notifications = NULL;
@@ -206,103 +162,83 @@ void ibutton_free(iButton* ibutton) {
furi_record_close(RECORD_GUI);
ibutton->gui = NULL;
- ibutton_worker_stop_thread(ibutton->key_worker);
- ibutton_worker_free(ibutton->key_worker);
+ ibutton_worker_stop_thread(ibutton->worker);
+ ibutton_worker_free(ibutton->worker);
ibutton_key_free(ibutton->key);
+ ibutton_protocols_free(ibutton->protocols);
furi_string_free(ibutton->file_path);
free(ibutton);
}
-bool ibutton_file_select(iButton* ibutton) {
- DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
- browser_options.base_path = IBUTTON_APP_FOLDER;
+bool ibutton_load_key(iButton* ibutton) {
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
- bool success = dialog_file_browser_show(
- ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options);
+ const bool success = ibutton_protocols_load(
+ ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path));
- if(success) {
- success = ibutton_load_key_data(ibutton, ibutton->file_path, true);
+ if(!success) {
+ dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
+
+ } else {
+ FuriString* tmp = furi_string_alloc();
+
+ path_extract_filename(ibutton->file_path, tmp, true);
+ strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE);
+
+ furi_string_free(tmp);
}
return success;
}
-bool ibutton_save_key(iButton* ibutton, const char* key_name) {
- // Create ibutton directory if necessary
+bool ibutton_select_and_load_key(iButton* ibutton) {
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
+ browser_options.base_path = IBUTTON_APP_FOLDER;
+
+ if(furi_string_empty(ibutton->file_path)) {
+ furi_string_set(ibutton->file_path, browser_options.base_path);
+ }
+
+ return dialog_file_browser_show(
+ ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) &&
+ ibutton_load_key(ibutton);
+}
+
+bool ibutton_save_key(iButton* ibutton) {
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
+
ibutton_make_app_folder(ibutton);
- FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
iButtonKey* key = ibutton->key;
+ const bool success =
+ ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path));
- bool result = false;
-
- do {
- // Check if we has old key
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- // First remove old key
- ibutton_delete_key(ibutton);
-
- // Remove old key name from path
- size_t filename_start = furi_string_search_rchar(ibutton->file_path, '/');
- furi_string_left(ibutton->file_path, filename_start);
- }
-
- furi_string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION);
-
- // Open file for write
- if(!flipper_format_file_open_always(file, furi_string_get_cstr(ibutton->file_path))) break;
-
- // Write header
- if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break;
-
- // Write key type
- if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
- break;
- const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
- if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
-
- // Write data
- if(!flipper_format_write_comment_cstr(
- file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
- break;
-
- if(!flipper_format_write_hex(
- file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
- break;
- result = true;
-
- } while(false);
-
- flipper_format_free(file);
-
- if(!result) { //-V547
+ if(!success) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file");
}
- return result;
+ return success;
}
bool ibutton_delete_key(iButton* ibutton) {
bool result = false;
- result = storage_simply_remove(ibutton->storage, furi_string_get_cstr(ibutton->file_path));
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path));
+ furi_record_close(RECORD_STORAGE);
+
+ ibutton_reset_key(ibutton);
return result;
}
-void ibutton_text_store_set(iButton* ibutton, const char* text, ...) {
- va_list args;
- va_start(args, text);
-
- vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args);
-
- va_end(args);
-}
-
-void ibutton_text_store_clear(iButton* ibutton) {
- memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1);
+void ibutton_reset_key(iButton* ibutton) {
+ memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1);
+ furi_string_reset(ibutton->file_path);
+ ibutton_key_reset(ibutton->key);
}
void ibutton_notification_message(iButton* ibutton, uint32_t message) {
@@ -310,36 +246,44 @@ void ibutton_notification_message(iButton* ibutton, uint32_t message) {
notification_message(ibutton->notifications, ibutton_notification_sequences[message]);
}
-int32_t ibutton_app(void* p) {
+void ibutton_submenu_callback(void* context, uint32_t index) {
+ iButton* ibutton = context;
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_widget_callback(GuiButtonType result, InputType type, void* context) {
+ iButton* ibutton = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+ }
+}
+
+int32_t ibutton_app(void* arg) {
iButton* ibutton = ibutton_alloc();
ibutton_make_app_folder(ibutton);
bool key_loaded = false;
- bool rpc_mode = false;
- if(p && strlen(p)) {
- uint32_t rpc_ctx = 0;
- if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
+ if((arg != NULL) && (strlen(arg) != 0)) {
+ if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) {
FURI_LOG_D(TAG, "Running in RPC mode");
- ibutton->rpc_ctx = (void*)rpc_ctx;
- rpc_mode = true;
- rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton);
- rpc_system_app_send_started(ibutton->rpc_ctx);
+
+ rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton);
+ rpc_system_app_send_started(ibutton->rpc);
+
} else {
- furi_string_set(ibutton->file_path, (const char*)p);
- if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) {
- key_loaded = true;
- // TODO: Display an error if the key from p could not be loaded
- }
+ furi_string_set(ibutton->file_path, (const char*)arg);
+ key_loaded = ibutton_load_key(ibutton);
}
}
- if(rpc_mode) {
+ if(ibutton->rpc != NULL) {
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);
@@ -353,9 +297,9 @@ int32_t ibutton_app(void* p) {
view_dispatcher_run(ibutton->view_dispatcher);
- if(ibutton->rpc_ctx) {
- rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
- rpc_system_app_send_exited(ibutton->rpc_ctx);
+ if(ibutton->rpc) {
+ rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
+ rpc_system_app_send_exited(ibutton->rpc);
}
ibutton_free(ibutton);
return 0;
diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c
index fab1ccf05..2b88b2007 100644
--- a/applications/main/ibutton/ibutton_cli.c
+++ b/applications/main/ibutton/ibutton_cli.c
@@ -1,11 +1,15 @@
#include
#include
-#include
+
#include
-#include
-#include
+#include
+
#include
+#include
+#include
+#include
+
static void ibutton_cli(Cli* cli, FuriString* args, void* context);
static void onewire_cli(Cli* cli, FuriString* args, void* context);
@@ -22,7 +26,7 @@ void ibutton_on_system_start() {
#endif
}
-void ibutton_cli_print_usage() {
+static void ibutton_cli_print_usage() {
printf("Usage:\r\n");
printf("ikey read\r\n");
printf("ikey emulate \r\n");
@@ -34,30 +38,52 @@ void ibutton_cli_print_usage() {
printf("\t are hex-formatted\r\n");
};
-bool ibutton_cli_get_key_type(FuriString* data, iButtonKeyType* type) {
+static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) {
bool result = false;
+ FuriString* name = furi_string_alloc();
- if(furi_string_cmp_str(data, "Dallas") == 0 || furi_string_cmp_str(data, "dallas") == 0) {
- result = true;
- *type = iButtonKeyDS1990;
- } else if(furi_string_cmp_str(data, "Cyfral") == 0 || furi_string_cmp_str(data, "cyfral") == 0) {
- result = true;
- *type = iButtonKeyCyfral;
- } else if(furi_string_cmp_str(data, "Metakom") == 0 || furi_string_cmp_str(data, "metakom") == 0) {
- result = true;
- *type = iButtonKeyMetakom;
- }
+ do {
+ // Read protocol name
+ if(!args_read_string_and_trim(args, name)) break;
+ // Make the protocol name uppercase
+ const char first = furi_string_get_char(name, 0);
+ furi_string_set_char(name, 0, toupper((int)first));
+
+ const iButtonProtocolId id =
+ ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name));
+ if(id == iButtonProtocolIdInvalid) break;
+
+ ibutton_key_set_protocol_id(key, id);
+
+ // Get the data pointer
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(protocols, key, &data);
+
+ // Read data
+ if(!args_read_hex_bytes(args, data.ptr, data.size)) break;
+
+ result = true;
+ } while(false);
+
+ furi_string_free(name);
return result;
}
-void ibutton_cli_print_key_data(iButtonKey* key) {
- const uint8_t* key_data = ibutton_key_get_data_p(key);
- iButtonKeyType type = ibutton_key_get_type(key);
+static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) {
+ const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key));
- printf("%s ", ibutton_key_get_string_by_type(type));
- for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) {
- printf("%02X", key_data[i]);
+ if(strncmp(name, "DS", 2) == 0) {
+ name = "Dallas";
+ }
+
+ printf("%s ", name);
+
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(protocols, key, &data);
+
+ for(size_t i = 0; i < data.size; i++) {
+ printf("%02X", data.ptr[i]);
}
printf("\r\n");
@@ -71,9 +97,10 @@ static void ibutton_cli_worker_read_cb(void* context) {
furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);
}
-void ibutton_cli_read(Cli* cli) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
+static void ibutton_cli_read(Cli* cli) {
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
FuriEventFlag* event = furi_event_flag_alloc();
ibutton_worker_start_thread(worker);
@@ -81,32 +108,25 @@ void ibutton_cli_read(Cli* cli) {
printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n");
ibutton_worker_read_start(worker, key);
+
while(true) {
uint32_t flags =
furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
- ibutton_cli_print_key_data(key);
-
- if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
- if(!ibutton_key_dallas_crc_is_valid(key)) {
- printf("Warning: invalid CRC\r\n");
- }
-
- if(!ibutton_key_dallas_is_1990_key(key)) {
- printf("Warning: not a key\r\n");
- }
- }
+ ibutton_cli_print_key(protocols, key);
break;
}
if(cli_cmd_interrupt_received(cli)) break;
}
- ibutton_worker_stop(worker);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
furi_event_flag_free(event);
};
@@ -124,48 +144,33 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult
}
void ibutton_cli_write(Cli* cli, FuriString* args) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
- iButtonKeyType type;
- iButtonWriteContext write_context;
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
- FuriString* data;
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
+ iButtonWriteContext write_context;
write_context.event = furi_event_flag_alloc();
- data = furi_string_alloc();
ibutton_worker_start_thread(worker);
ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);
do {
- if(!args_read_string_and_trim(args, data)) {
+ if(!ibutton_cli_parse_key(protocols, key, args)) {
ibutton_cli_print_usage();
break;
}
- if(!ibutton_cli_get_key_type(data, &type)) {
+ if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) &
+ iButtonProtocolFeatureWriteBlank)) {
ibutton_cli_print_usage();
break;
}
- if(type != iButtonKeyDS1990) {
- ibutton_cli_print_usage();
- break;
- }
-
- if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
- ibutton_cli_print_usage();
- break;
- }
-
- ibutton_key_set_type(key, type);
- ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
-
printf("Writing key ");
- ibutton_cli_print_key_data(key);
+ ibutton_cli_print_key(protocols, key);
printf("Press Ctrl+C to abort\r\n");
- ibutton_worker_write_start(worker, key);
+ ibutton_worker_write_blank_start(worker, key);
while(true) {
uint32_t flags = furi_event_flag_wait(
write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
@@ -183,64 +188,53 @@ void ibutton_cli_write(Cli* cli, FuriString* args) {
if(cli_cmd_interrupt_received(cli)) break;
}
- ibutton_worker_stop(worker);
} while(false);
- furi_string_free(data);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
furi_event_flag_free(write_context.event);
-};
+}
void ibutton_cli_emulate(Cli* cli, FuriString* args) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
- iButtonKeyType type;
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
- FuriString* data;
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
- data = furi_string_alloc();
ibutton_worker_start_thread(worker);
do {
- if(!args_read_string_and_trim(args, data)) {
+ if(!ibutton_cli_parse_key(protocols, key, args)) {
ibutton_cli_print_usage();
break;
}
- if(!ibutton_cli_get_key_type(data, &type)) {
- ibutton_cli_print_usage();
- break;
- }
-
- if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
- ibutton_cli_print_usage();
- break;
- }
-
- ibutton_key_set_type(key, type);
- ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
-
printf("Emulating key ");
- ibutton_cli_print_key_data(key);
+ ibutton_cli_print_key(protocols, key);
printf("Press Ctrl+C to abort\r\n");
ibutton_worker_emulate_start(worker, key);
+
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(100);
};
- ibutton_worker_stop(worker);
+
} while(false);
- furi_string_free(data);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
};
-static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
+void ibutton_cli(Cli* cli, FuriString* args, void* context) {
+ UNUSED(cli);
UNUSED(context);
FuriString* cmd;
cmd = furi_string_alloc();
@@ -264,14 +258,14 @@ static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
furi_string_free(cmd);
}
-void onewire_cli_print_usage() {
+static void onewire_cli_print_usage() {
printf("Usage:\r\n");
printf("onewire search\r\n");
};
static void onewire_cli_search(Cli* cli) {
UNUSED(cli);
- OneWireHost* onewire = onewire_host_alloc();
+ OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
uint8_t address[8];
bool done = false;
@@ -281,7 +275,7 @@ static void onewire_cli_search(Cli* cli) {
furi_hal_power_enable_otg();
while(!done) {
- if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) {
+ if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
printf("Search finished\r\n");
onewire_host_reset_search(onewire);
done = true;
diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h
index 1e2f0300d..28bcb94a0 100644
--- a/applications/main/ibutton/ibutton_custom_event.h
+++ b/applications/main/ibutton/ibutton_custom_event.h
@@ -6,6 +6,7 @@ enum iButtonCustomEvent {
iButtonCustomEventBack,
iButtonCustomEventTextEditResult,
+ iButtonCustomEventByteEditChanged,
iButtonCustomEventByteEditResult,
iButtonCustomEventWorkerEmulated,
iButtonCustomEventWorkerRead,
diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h
index 0a8099351..8ad0b90e4 100644
--- a/applications/main/ibutton/ibutton_i.h
+++ b/applications/main/ibutton/ibutton_i.h
@@ -4,31 +4,40 @@
#include
#include
-#include
-#include
#include
-#include
+#include
#include
+#include
+
+#include
#include
#include
+#include
+#include
#include
#include
-#include
#include
#include
#include
+#include
+
+#include
#include "ibutton_custom_event.h"
#include "scenes/ibutton_scene.h"
-#define IBUTTON_FILE_NAME_SIZE 100
-#define IBUTTON_TEXT_STORE_SIZE 128
-
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
#define IBUTTON_APP_EXTENSION ".ibtn"
-#define IBUTTON_APP_FILE_TYPE "Flipper iButton key"
+
+#define IBUTTON_KEY_NAME_SIZE 22
+
+typedef enum {
+ iButtonWriteModeInvalid,
+ iButtonWriteModeBlank,
+ iButtonWriteModeCopy,
+} iButtonWriteMode;
struct iButton {
SceneManager* scene_manager;
@@ -38,21 +47,22 @@ struct iButton {
Storage* storage;
DialogsApp* dialogs;
NotificationApp* notifications;
+ RpcAppSystem* rpc;
- iButtonWorker* key_worker;
iButtonKey* key;
+ iButtonWorker* worker;
+ iButtonProtocols* protocols;
+ iButtonWriteMode write_mode;
FuriString* file_path;
- char text_store[IBUTTON_TEXT_STORE_SIZE + 1];
+ char key_name[IBUTTON_KEY_NAME_SIZE + 1];
Submenu* submenu;
ByteInput* byte_input;
TextInput* text_input;
Popup* popup;
Widget* widget;
- DialogEx* dialog_ex;
-
- void* rpc_ctx;
+ Loading* loading;
};
typedef enum {
@@ -61,7 +71,7 @@ typedef enum {
iButtonViewTextInput,
iButtonViewPopup,
iButtonViewWidget,
- iButtonViewDialogEx,
+ iButtonViewLoading,
} iButtonView;
typedef enum {
@@ -78,10 +88,12 @@ typedef enum {
iButtonNotificationMessageBlinkStop,
} iButtonNotificationMessage;
-bool ibutton_file_select(iButton* ibutton);
-bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog);
-bool ibutton_save_key(iButton* ibutton, const char* key_name);
+bool ibutton_select_and_load_key(iButton* ibutton);
+bool ibutton_load_key(iButton* ibutton);
+bool ibutton_save_key(iButton* ibutton);
bool ibutton_delete_key(iButton* ibutton);
-void ibutton_text_store_set(iButton* ibutton, const char* text, ...);
-void ibutton_text_store_clear(iButton* ibutton);
+void ibutton_reset_key(iButton* ibutton);
void ibutton_notification_message(iButton* ibutton, uint32_t message);
+
+void ibutton_submenu_callback(void* context, uint32_t index);
+void ibutton_widget_callback(GuiButtonType result, InputType type, void* context);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_type.c b/applications/main/ibutton/scenes/ibutton_scene_add_type.c
index 38373999c..55e05f20d 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_add_type.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_add_type.c
@@ -1,54 +1,57 @@
#include "../ibutton_i.h"
-enum SubmenuIndex {
- SubmenuIndexCyfral,
- SubmenuIndexDallas,
- SubmenuIndexMetakom,
-};
-
-void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_add_type_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton);
+ FuriString* tmp = furi_string_alloc();
- submenu_set_selected_item(
- submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType));
+ for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count();
+ ++protocol_id) {
+ if((strcmp(
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) &&
+ (strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") !=
+ 0)) {
+ furi_string_printf(
+ tmp,
+ "%s %s",
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
+ } else {
+ furi_string_printf(
+ tmp, "%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id));
+ }
+
+ submenu_add_item(
+ submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context);
+ }
+
+ const uint32_t prev_protocol_id =
+ scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType);
+ submenu_set_selected_item(submenu, prev_protocol_id);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+ furi_string_free(tmp);
}
bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
iButtonKey* key = ibutton->key;
+
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, event.event);
- consumed = true;
- if(event.event == SubmenuIndexCyfral) {
- ibutton_key_set_type(key, iButtonKeyCyfral);
- } else if(event.event == SubmenuIndexDallas) {
- ibutton_key_set_type(key, iButtonKeyDS1990);
- } else if(event.event == SubmenuIndexMetakom) {
- ibutton_key_set_type(key, iButtonKeyMetakom);
- } else {
- furi_crash("Unknown key type");
- }
+ const iButtonProtocolId protocol_id = event.event;
- furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
- ibutton_key_clear_data(key);
+ ibutton_key_reset(key);
+ ibutton_key_set_protocol_id(key, protocol_id);
+ ibutton_protocols_apply_edits(ibutton->protocols, key);
+
+ scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+
+ consumed = true;
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
index ccac76121..dc340771b 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
@@ -1,42 +1,52 @@
#include "../ibutton_i.h"
-void ibutton_scene_add_type_byte_input_callback(void* context) {
+static void ibutton_scene_add_type_byte_input_callback(void* context) {
iButton* ibutton = context;
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);
}
+static void ibutton_scene_add_type_byte_changed_callback(void* context) {
+ iButton* ibutton = context;
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged);
+}
+
void ibutton_scene_add_value_on_enter(void* context) {
iButton* ibutton = context;
- iButtonKey* key = ibutton->key;
- uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE);
+ byte_input_set_header_text(ibutton->byte_input, "Enter the key");
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data);
- memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
+ iButtonEditableData editable_data;
+ ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data);
byte_input_set_result_callback(
ibutton->byte_input,
ibutton_scene_add_type_byte_input_callback,
- NULL,
- ibutton,
- new_key_data,
- ibutton_key_get_data_size(key));
+ ibutton_scene_add_type_byte_changed_callback,
+ context,
+ editable_data.ptr,
+ editable_data.size);
- byte_input_set_header_text(ibutton->byte_input, "Enter the key");
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput);
}
bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
- uint8_t* new_key_data =
- (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventByteEditResult) {
- ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE);
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+ scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
+ } else if(event.event == iButtonCustomEventByteEditChanged) {
+ ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ // User cancelled editing, reload the key from storage
+ if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) {
+ if(!ibutton_load_key(ibutton)) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ scene_manager, iButtonSceneStart);
+ }
}
}
@@ -45,10 +55,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_add_value_on_exit(void* context) {
iButton* ibutton = context;
- uint8_t* new_key_data =
- (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(ibutton->byte_input, NULL);
- free(new_key_data);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_config.h b/applications/main/ibutton/scenes/ibutton_scene_config.h
index 87fa1a036..79f6791b3 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_config.h
+++ b/applications/main/ibutton/scenes/ibutton_scene_config.h
@@ -6,8 +6,7 @@ ADD_SCENE(ibutton, info, Info)
ADD_SCENE(ibutton, read, Read)
ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu)
ADD_SCENE(ibutton, read_success, ReadSuccess)
-ADD_SCENE(ibutton, read_crc_error, ReadCRCError)
-ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError)
+ADD_SCENE(ibutton, read_error, ReadError)
ADD_SCENE(ibutton, select_key, SelectKey)
ADD_SCENE(ibutton, add_type, AddType)
ADD_SCENE(ibutton, add_value, AddValue)
@@ -18,4 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
ADD_SCENE(ibutton, delete_success, DeleteSuccess)
ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
ADD_SCENE(ibutton, exit_confirm, ExitConfirm)
+ADD_SCENE(ibutton, view_data, ViewData)
ADD_SCENE(ibutton, rpc, Rpc)
diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
index 3d609e833..587cb748c 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
@@ -1,74 +1,29 @@
#include "../ibutton_i.h"
#include
-static void ibutton_scene_delete_confirm_widget_callback(
- GuiButtonType result,
- InputType type,
- void* context) {
- iButton* ibutton = context;
- if(type == InputTypeShort) {
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
- }
-}
-
void ibutton_scene_delete_confirm_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
- FuriString* key_name;
- key_name = furi_string_alloc();
- path_extract_filename(ibutton->file_path, key_name, true);
+ FuriString* tmp = furi_string_alloc();
- ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", furi_string_get_cstr(key_name));
- widget_add_text_box_element(
- widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context);
widget_add_button_element(
- widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton);
- widget_add_button_element(
- widget,
- GuiButtonTypeRight,
- "Delete",
- ibutton_scene_delete_confirm_widget_callback,
- ibutton);
+ widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Dallas");
- break;
-
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
- break;
-
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Metakom");
- break;
- }
+ furi_string_printf(tmp, "Delete %s?", ibutton->key_name);
widget_add_string_element(
- widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
+ widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
+
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- furi_string_free(key_name);
+ furi_string_free(tmp);
}
bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
@@ -81,8 +36,10 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
if(event.event == GuiButtonTypeRight) {
if(ibutton_delete_key(ibutton)) {
scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess);
+ } else {
+ dialog_message_show_storage_error(ibutton->dialogs, "Cannot delete\nkey file");
+ scene_manager_previous_scene(scene_manager);
}
- //TODO: What if the key could not be deleted?
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(scene_manager);
}
@@ -93,6 +50,5 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
void ibutton_scene_delete_confirm_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_text_store_clear(ibutton);
widget_reset(ibutton->widget);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
index 6f6ffcf57..713b8331c 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
@@ -14,61 +14,32 @@ static void ibutton_scene_emulate_callback(void* context, bool emulated) {
void ibutton_scene_emulate_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
+ FuriString* tmp = furi_string_alloc();
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- // check that stored key has name
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
- } else {
- // if not, show key data
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- break;
- }
- }
+ furi_string_printf(
+ tmp,
+ "%s\n[%s]",
+ furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
+
+ widget_add_text_box_element(
+ widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
widget_add_string_multiline_element(
- widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
- widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- widget_add_text_box_element(
- widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- ibutton_worker_emulate_set_callback(
- ibutton->key_worker, ibutton_scene_emulate_callback, ibutton);
- ibutton_worker_emulate_start(ibutton->key_worker, key);
-
- furi_string_free(key_name);
+ ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
+ ibutton_worker_emulate_start(ibutton->worker, key);
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
@@ -78,8 +49,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeTick) {
uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate);
if(cnt > 0) {
- cnt--;
- if(cnt == 0) {
+ if(--cnt == 0) {
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);
}
scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt);
@@ -101,7 +71,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_emulate_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton_worker_stop(ibutton->worker);
widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c
index 2367e1217..9029a4b7b 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c
@@ -19,7 +19,7 @@ void ibutton_scene_exit_confirm_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeRight, "Stay", ibutton_scene_exit_confirm_widget_callback, ibutton);
widget_add_string_element(
- widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu?");
+ widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton Menu?");
widget_add_string_element(
widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
diff --git a/applications/main/ibutton/scenes/ibutton_scene_info.c b/applications/main/ibutton/scenes/ibutton_scene_info.c
index 15648f6f2..cf44d6a86 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_info.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_info.c
@@ -1,66 +1,54 @@
#include "../ibutton_i.h"
-#include
void ibutton_scene_info_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
+ Widget* widget = ibutton->widget;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
- FuriString* key_name;
- key_name = furi_string_alloc();
- path_extract_filename(ibutton->file_path, key_name, true);
+ FuriString* tmp = furi_string_alloc();
+
+ furi_string_printf(
+ tmp,
+ "\e#%s [%s]\e#",
+ ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
widget_add_text_box_element(
- widget, 0, 0, 128, 23, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Dallas");
- break;
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- widget_add_string_element(
- widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Metakom");
- break;
+ widget_add_string_multiline_element(
+ widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Cyfral");
- break;
+ if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
+ iButtonProtocolFeatureExtData) {
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
}
- widget_add_string_element(
- widget, 64, 50, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- furi_string_free(key_name);
+ furi_string_free(tmp);
}
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
- return false;
+ iButton* ibutton = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ if(event.event == GuiButtonTypeRight) {
+ scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData);
+ }
+ }
+
+ return consumed;
}
void ibutton_scene_info_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_text_store_clear(ibutton);
widget_reset(ibutton->widget);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c
index 1ccd2562b..a2b3b53e4 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read.c
@@ -11,14 +11,13 @@ void ibutton_scene_read_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
iButtonKey* key = ibutton->key;
- iButtonWorker* worker = ibutton->key_worker;
+ iButtonWorker* worker = ibutton->worker;
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
- popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
+ popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 5, XTREME_ASSETS()->I_DolphinWait_61x59);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
- furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton);
ibutton_worker_read_start(worker, key);
@@ -36,25 +35,14 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventWorkerRead) {
- bool success = false;
- iButtonKey* key = ibutton->key;
-
- if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
- if(!ibutton_key_dallas_crc_is_valid(key)) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError);
- } else if(!ibutton_key_dallas_is_1990_key(key)) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError);
- } else {
- success = true;
- }
- } else {
- success = true;
- }
-
- if(success) {
+ if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) {
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
+
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
+
+ } else {
+ scene_manager_next_scene(scene_manager, iButtonSceneReadError);
}
}
}
@@ -65,7 +53,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_read_on_exit(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton_worker_stop(ibutton->worker);
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);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c
deleted file mode 100644
index f822ff6a2..000000000
--- a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "../ibutton_i.h"
-#include
-
-static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
-
-void ibutton_scene_read_crc_error_on_enter(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
-
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7],
- maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
- dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageError);
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
-}
-
-bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) {
- iButton* ibutton = context;
- SceneManager* scene_manager = ibutton->scene_manager;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeBack) {
- consumed = true;
- scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
- } else if(event.type == SceneManagerEventTypeCustom) {
- consumed = true;
- if(event.event == DialogExResultRight) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
- scene_manager_previous_scene(scene_manager);
- }
- }
-
- return consumed;
-}
-
-void ibutton_scene_read_crc_error_on_exit(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
-
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
-}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_error.c
new file mode 100644
index 000000000..e966384bf
--- /dev/null
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_error.c
@@ -0,0 +1,58 @@
+#include "../ibutton_i.h"
+#include
+
+void ibutton_scene_read_error_on_enter(void* context) {
+ iButton* ibutton = context;
+ iButtonKey* key = ibutton->key;
+
+ Widget* widget = ibutton->widget;
+
+ FuriString* tmp = furi_string_alloc();
+
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
+
+ widget_add_string_element(
+ widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error");
+
+ ibutton_protocols_render_error(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
+
+ ibutton_notification_message(ibutton, iButtonNotificationMessageError);
+ ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+ furi_string_free(tmp);
+}
+
+bool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) {
+ iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeBack) {
+ consumed = true;
+ scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
+
+ } else if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ if(event.event == GuiButtonTypeLeft) {
+ scene_manager_previous_scene(scene_manager);
+ } else if(event.event == GuiButtonTypeRight) {
+ scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
+ }
+ }
+
+ return consumed;
+}
+
+void ibutton_scene_read_error_on_exit(void* context) {
+ iButton* ibutton = context;
+
+ ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
+ widget_reset(ibutton->widget);
+}
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 0a8ecfa55..716f72c7d 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
@@ -4,7 +4,9 @@
typedef enum {
SubmenuIndexSave,
SubmenuIndexEmulate,
- SubmenuIndexWrite,
+ SubmenuIndexViewData,
+ SubmenuIndexWriteBlank,
+ SubmenuIndexWriteCopy,
} SubmenuIndex;
void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {
@@ -16,6 +18,9 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key);
+ const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id);
+
submenu_add_item(
submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton);
submenu_add_item(
@@ -24,36 +29,66 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
SubmenuIndexEmulate,
ibutton_scene_read_key_menu_submenu_callback,
ibutton);
- if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+
+ if(features & iButtonProtocolFeatureExtData) {
submenu_add_item(
submenu,
- "Write",
- SubmenuIndexWrite,
+ "View Data",
+ SubmenuIndexViewData,
ibutton_scene_read_key_menu_submenu_callback,
ibutton);
}
+
+ if(features & iButtonProtocolFeatureWriteBlank) {
+ submenu_add_item(
+ submenu,
+ "Write Blank",
+ SubmenuIndexWriteBlank,
+ ibutton_scene_read_key_menu_submenu_callback,
+ ibutton);
+ }
+
+ if(features & iButtonProtocolFeatureWriteCopy) {
+ submenu_add_item(
+ submenu,
+ "Write Copy",
+ SubmenuIndexWriteCopy,
+ ibutton_scene_read_key_menu_submenu_callback,
+ ibutton);
+ }
+
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
}
bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event);
+ scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event);
consumed = true;
+
if(event.event == SubmenuIndexSave) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+ scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
} else if(event.event == SubmenuIndexEmulate) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
- } else if(event.event == SubmenuIndexWrite) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexViewData) {
+ scene_manager_next_scene(scene_manager, iButtonSceneViewData);
+ } else if(event.event == SubmenuIndexWriteBlank) {
+ ibutton->write_mode = iButtonWriteModeBlank;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteCopy) {
+ ibutton->write_mode = iButtonWriteModeCopy;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
}
+ } else if(event.event == SceneManagerEventTypeBack) {
+ scene_manager_set_scene_state(
+ ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave);
+ // Event is not consumed
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c
deleted file mode 100644
index 8a7528031..000000000
--- a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "../ibutton_i.h"
-#include
-
-static void
- ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
-
-void ibutton_scene_read_not_key_error_on_enter(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
-
- ibutton_text_store_set(
- ibutton,
- "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7],
- maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
- dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageError);
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
-}
-
-bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) {
- iButton* ibutton = context;
- SceneManager* scene_manager = ibutton->scene_manager;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeBack) {
- consumed = true;
- scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
- } else if(event.type == SceneManagerEventTypeCustom) {
- consumed = true;
- if(event.event == DialogExResultRight) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
- scene_manager_previous_scene(scene_manager);
- }
- }
-
- return consumed;
-}
-
-void ibutton_scene_read_not_key_error_on_exit(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
-
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
-}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_success.c b/applications/main/ibutton/scenes/ibutton_scene_read_success.c
index 749e7af37..2e50bc996 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_success.c
@@ -1,55 +1,40 @@
#include "../ibutton_i.h"
-#include
-static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
+#include
void ibutton_scene_read_success_on_enter(void* context) {
iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton,
- "Metakom\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3]);
- break;
- }
+ FuriString* tmp = furi_string_alloc();
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
+ furi_string_printf(
+ tmp,
+ "%s[%s]",
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id));
+
+ widget_add_string_element(
+ widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
+
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {
@@ -62,9 +47,9 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
- if(event.event == DialogExResultRight) {
+ if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
+ } else if(event.event == GuiButtonTypeLeft) {
scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm);
}
}
@@ -74,11 +59,8 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
void ibutton_scene_read_success_on_exit(void* context) {
iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
+ widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c
index 7f8c95b1e..34de5b877 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c
@@ -19,7 +19,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton);
widget_add_string_element(
- widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
+ widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
widget_add_string_element(
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c
index b25b1b8dd..9205eb4b4 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c
@@ -1,6 +1,4 @@
#include "../ibutton_i.h"
-#include
-#include
void ibutton_scene_rpc_on_enter(void* context) {
iButton* ibutton = context;
@@ -17,8 +15,6 @@ void ibutton_scene_rpc_on_enter(void* context) {
}
bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
iButton* ibutton = context;
Popup* popup = ibutton->popup;
@@ -26,40 +22,32 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
- if(event.event == iButtonCustomEventRpcLoad) {
- const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx);
- bool result = false;
- if(arg && (furi_string_empty(ibutton->file_path))) {
- furi_string_set(ibutton->file_path, arg);
- if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) {
- ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(
- ibutton, "emulating\n%s", furi_string_get_cstr(key_name));
- } else {
- ibutton_text_store_set(ibutton, "emulating");
- }
- popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
+ if(event.event == iButtonCustomEventRpcLoad) {
+ bool result = false;
+ const char* file_path = rpc_system_app_get_data(ibutton->rpc);
+
+ if(file_path && (furi_string_empty(ibutton->file_path))) {
+ furi_string_set(ibutton->file_path, file_path);
+
+ if(ibutton_load_key(ibutton)) {
+ popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
- furi_string_free(key_name);
result = true;
- } else {
- furi_string_reset(ibutton->file_path);
}
}
- rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result);
+
+ rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
+
} else if(event.event == iButtonCustomEventRpcExit) {
- rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true);
+ rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true);
scene_manager_stop(ibutton->scene_manager);
view_dispatcher_stop(ibutton->view_dispatcher);
+
} else if(event.event == iButtonCustomEventRpcSessionClose) {
scene_manager_stop(ibutton->scene_manager);
view_dispatcher_stop(ibutton->view_dispatcher);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
index 5f25a0002..4ad0315e5 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
@@ -1,6 +1,8 @@
#include "../ibutton_i.h"
-#include
+
+#include
#include
+
#include
static void ibutton_scene_save_name_text_input_callback(void* context) {
@@ -12,17 +14,10 @@ void ibutton_scene_save_name_on_enter(void* context) {
iButton* ibutton = context;
TextInput* text_input = ibutton->text_input;
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ const bool is_new_file = furi_string_empty(ibutton->file_path);
- const bool key_name_is_empty = furi_string_empty(key_name);
- if(key_name_is_empty) {
- set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE);
- } else {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
+ if(is_new_file) {
+ set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE);
}
text_input_set_header_text(text_input, "Name the key");
@@ -30,23 +25,15 @@ void ibutton_scene_save_name_on_enter(void* context) {
text_input,
ibutton_scene_save_name_text_input_callback,
ibutton,
- ibutton->text_store,
+ ibutton->key_name,
IBUTTON_KEY_NAME_SIZE,
- key_name_is_empty);
+ is_new_file);
- FuriString* folder_path;
- folder_path = furi_string_alloc();
-
- path_extract_dirname(furi_string_get_cstr(ibutton->file_path), folder_path);
-
- ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
- furi_string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, furi_string_get_cstr(key_name));
+ ValidatorIsFile* validator_is_file =
+ validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);
-
- furi_string_free(key_name);
- furi_string_free(folder_path);
}
bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
@@ -56,8 +43,16 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventTextEditResult) {
- if(ibutton_save_key(ibutton, ibutton->text_store)) {
+ furi_string_printf(
+ ibutton->file_path,
+ "%s/%s%s",
+ IBUTTON_APP_FOLDER,
+ ibutton->key_name,
+ IBUTTON_APP_EXTENSION);
+
+ if(ibutton_save_key(ibutton)) {
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
@@ -67,6 +62,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else {
DOLPHIN_DEED(DolphinDeedIbuttonSave);
}
+
} else {
const uint32_t possible_scenes[] = {
iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
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 e4c9c350a..80fca28b5 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
@@ -3,72 +3,70 @@
enum SubmenuIndex {
SubmenuIndexEmulate,
- SubmenuIndexWrite,
+ SubmenuIndexWriteBlank,
+ SubmenuIndexWriteCopy,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexInfo,
};
-void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_saved_key_menu_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu,
- "Emulate",
- SubmenuIndexEmulate,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
- if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+ const uint32_t features = ibutton_protocols_get_features(
+ ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key));
+
+ submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton);
+
+ if(features & iButtonProtocolFeatureWriteBlank) {
submenu_add_item(
- submenu,
- "Write",
- SubmenuIndexWrite,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
+ submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
}
- submenu_add_item(
- submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
- submenu_add_item(
- submenu,
- "Delete",
- SubmenuIndexDelete,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
- submenu_add_item(
- submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
+
+ if(features & iButtonProtocolFeatureWriteCopy) {
+ submenu_add_item(
+ submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton);
+ }
+
+ submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Delete", SubmenuIndexDelete, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Info", SubmenuIndexInfo, ibutton_submenu_callback, ibutton);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu));
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
}
bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event);
+ scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event);
consumed = true;
if(event.event == SubmenuIndexEmulate) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
- } else if(event.event == SubmenuIndexWrite) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteBlank) {
+ ibutton->write_mode = iButtonWriteModeBlank;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteCopy) {
+ ibutton->write_mode = iButtonWriteModeCopy;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
} else if(event.event == SubmenuIndexEdit) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+ scene_manager_next_scene(scene_manager, iButtonSceneAddValue);
} else if(event.event == SubmenuIndexDelete) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm);
+ scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm);
} else if(event.event == SubmenuIndexInfo) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo);
+ scene_manager_next_scene(scene_manager, iButtonSceneInfo);
}
+
+ } else if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_set_scene_state(
+ scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate);
+ // Event is not consumed
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_select_key.c b/applications/main/ibutton/scenes/ibutton_scene_select_key.c
index 32169a9c0..ebd504b1f 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_select_key.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_select_key.c
@@ -3,11 +3,11 @@
void ibutton_scene_select_key_on_enter(void* context) {
iButton* ibutton = context;
- if(!ibutton_file_select(ibutton)) {
+ if(ibutton_select_and_load_key(ibutton)) {
+ scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
+ } else {
scene_manager_search_and_switch_to_previous_scene(
ibutton->scene_manager, iButtonSceneStart);
- } else {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
}
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c
index 28807faa8..37bf96f39 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_start.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_start.c
@@ -8,21 +8,15 @@ enum SubmenuIndex {
SubmenuIndexAdd,
};
-void ibutton_scene_start_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_start_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton);
+ ibutton_reset_key(ibutton);
+
+ submenu_add_item(submenu, "Read", SubmenuIndexRead, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Saved", SubmenuIndexSaved, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, ibutton_submenu_callback, ibutton);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart));
@@ -39,9 +33,8 @@ 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);
+ 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);
} else if(event.event == SubmenuIndexAdd) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_view_data.c b/applications/main/ibutton/scenes/ibutton_scene_view_data.c
new file mode 100644
index 000000000..7e063d7ec
--- /dev/null
+++ b/applications/main/ibutton/scenes/ibutton_scene_view_data.c
@@ -0,0 +1,26 @@
+#include "../ibutton_i.h"
+
+void ibutton_scene_view_data_on_enter(void* context) {
+ iButton* ibutton = context;
+ iButtonKey* key = ibutton->key;
+ Widget* widget = ibutton->widget;
+
+ FuriString* tmp = furi_string_alloc();
+ ibutton_protocols_render_data(ibutton->protocols, key, tmp);
+
+ widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp));
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+ furi_string_free(tmp);
+}
+
+bool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void ibutton_scene_view_data_on_exit(void* context) {
+ iButton* ibutton = context;
+ widget_reset(ibutton->widget);
+}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_write.c b/applications/main/ibutton/scenes/ibutton_scene_write.c
index cdea04db3..541aa1c52 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_write.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_write.c
@@ -1,5 +1,4 @@
#include "../ibutton_i.h"
-#include "toolbox/path.h"
typedef enum {
iButtonSceneWriteStateDefault,
@@ -13,61 +12,46 @@ static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult
void ibutton_scene_write_on_enter(void* context) {
iButton* ibutton = context;
+ furi_assert(ibutton->write_mode != iButtonWriteModeInvalid);
+
iButtonKey* key = ibutton->key;
+ iButtonWorker* worker = ibutton->worker;
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
+
Widget* widget = ibutton->widget;
- iButtonWorker* worker = ibutton->key_worker;
+ FuriString* tmp = furi_string_alloc();
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ furi_string_printf(
+ tmp,
+ "%s\n[%s]",
+ ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
- // check that stored key has name
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
- } else {
- // if not, show key data
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- break;
- }
+ widget_add_text_box_element(
+ widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
+
+ ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
+
+ furi_string_set(tmp, "iButton\nwriting ");
+
+ if(ibutton->write_mode == iButtonWriteModeBlank) {
+ furi_string_cat(tmp, "Blank");
+ ibutton_worker_write_blank_start(worker, key);
+
+ } else if(ibutton->write_mode == iButtonWriteModeCopy) {
+ furi_string_cat(tmp, "Copy");
+ ibutton_worker_write_copy_start(worker, key);
}
widget_add_string_multiline_element(
- widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nwriting");
- widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- widget_add_text_box_element(
- widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
- ibutton_worker_write_start(worker, key);
-
- furi_string_free(key_name);
+ widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
@@ -94,7 +78,9 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_write_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton->write_mode = iButtonWriteModeInvalid;
+
+ ibutton_worker_stop(ibutton->worker);
widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c
index 9d78a09b6..a88306cc5 100644
--- a/applications/main/infrared/infrared.c
+++ b/applications/main/infrared/infrared.c
@@ -3,6 +3,8 @@
#include
#include
+#define INFRARED_TX_MIN_INTERVAL_MS 50U
+
static const NotificationSequence* infrared_notification_sequences[] = {
&sequence_success,
&sequence_set_only_green_255,
@@ -148,6 +150,12 @@ static Infrared* infrared_alloc() {
view_dispatcher_add_view(
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
+ infrared->variable_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ infrared->view_dispatcher,
+ InfraredViewVariableItemList,
+ variable_item_list_get_view(infrared->variable_item_list));
+
infrared->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
@@ -195,6 +203,9 @@ static void infrared_free(Infrared* infrared) {
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
text_input_free(infrared->text_input);
+ view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
+ variable_item_list_free(infrared->variable_item_list);
+
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex);
@@ -299,10 +310,13 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
if(infrared->app_state.is_transmitting) {
- FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active");
return;
- } else {
- infrared->app_state.is_transmitting = true;
+ }
+
+ const uint32_t time_elapsed = furi_get_tick() - infrared->app_state.last_transmit_time;
+
+ if(time_elapsed < INFRARED_TX_MIN_INTERVAL_MS) {
+ return;
}
if(infrared_signal_is_raw(signal)) {
@@ -319,6 +333,8 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
infrared_worker_tx_set_get_signal_callback(
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
infrared_worker_tx_start(infrared->worker);
+
+ infrared->app_state.is_transmitting = true;
}
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
@@ -328,26 +344,24 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
InfraredSignal* signal = infrared_remote_button_get_signal(button);
infrared_tx_start_signal(infrared, signal);
- infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
}
void infrared_tx_start_received(Infrared* infrared) {
infrared_tx_start_signal(infrared, infrared->received_signal);
- infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
}
void infrared_tx_stop(Infrared* infrared) {
if(!infrared->app_state.is_transmitting) {
- FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped");
return;
- } else {
- infrared->app_state.is_transmitting = false;
}
infrared_worker_tx_stop(infrared->worker);
infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
+
+ infrared->app_state.is_transmitting = false;
+ infrared->app_state.last_transmit_time = furi_get_tick();
}
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c
index 5f5e2d4bb..3fa99cb02 100644
--- a/applications/main/infrared/infrared_cli.c
+++ b/applications/main/infrared/infrared_cli.c
@@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) {
printf("\tir universal \r\n");
printf("\tir universal list \r\n");
// TODO: Do not hardcode universal remote names
- printf("\tAvailable universal remotes: tv audio ac\r\n");
+ printf("\tAvailable universal remotes: tv audio ac projector\r\n");
}
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h
index 6972d53b4..55b70fc5e 100644
--- a/applications/main/infrared/infrared_i.h
+++ b/applications/main/infrared/infrared_i.h
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
#include
#include
@@ -9,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -32,8 +34,6 @@
#include "rpc/rpc_app.h"
-#include
-
#define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128
@@ -70,6 +70,7 @@ typedef struct {
InfraredEditTarget edit_target : 8;
InfraredEditMode edit_mode : 8;
int32_t current_button_index;
+ uint32_t last_transmit_time;
} InfraredAppState;
struct Infrared {
@@ -87,6 +88,7 @@ struct Infrared {
Submenu* submenu;
TextInput* text_input;
+ VariableItemList* variable_item_list;
DialogEx* dialog_ex;
ButtonMenu* button_menu;
Popup* popup;
@@ -108,6 +110,7 @@ struct Infrared {
typedef enum {
InfraredViewSubmenu,
InfraredViewTextInput,
+ InfraredViewVariableItemList,
InfraredViewDialogEx,
InfraredViewButtonMenu,
InfraredViewPopup,
diff --git a/applications/main/infrared/scenes/infrared_scene_ask_back.c b/applications/main/infrared/scenes/infrared_scene_ask_back.c
index 493458ade..77fc97f98 100644
--- a/applications/main/infrared/scenes/infrared_scene_ask_back.c
+++ b/applications/main/infrared/scenes/infrared_scene_ask_back.c
@@ -10,13 +10,13 @@ void infrared_scene_ask_back_on_enter(void* context) {
DialogEx* dialog_ex = infrared->dialog_ex;
if(infrared->app_state.is_learning_new_remote) {
- dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop);
+ dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 11, AlignCenter, AlignTop);
} else {
- dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop);
+ dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 11, AlignCenter, AlignTop);
}
dialog_ex_set_text(
- dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
+ dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_center_button_text(dialog_ex, NULL);
diff --git a/applications/main/infrared/scenes/infrared_scene_ask_retry.c b/applications/main/infrared/scenes/infrared_scene_ask_retry.c
index c87d9e6d3..602e470c7 100644
--- a/applications/main/infrared/scenes/infrared_scene_ask_retry.c
+++ b/applications/main/infrared/scenes/infrared_scene_ask_retry.c
@@ -9,9 +9,9 @@ void infrared_scene_ask_retry_on_enter(void* context) {
Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex;
- dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop);
+ dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
dialog_ex_set_text(
- dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter);
+ dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_center_button_text(dialog_ex, NULL);
diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h
index 551485295..b15974941 100644
--- a/applications/main/infrared/scenes/infrared_scene_config.h
+++ b/applications/main/infrared/scenes/infrared_scene_config.h
@@ -21,4 +21,5 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)
+ADD_SCENE(infrared, debug_settings, DebugSettings)
ADD_SCENE(infrared, rpc, Rpc)
diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c
new file mode 100644
index 000000000..0bc830788
--- /dev/null
+++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c
@@ -0,0 +1,59 @@
+#include "../infrared_i.h"
+#include
+
+uint8_t value_index_ir;
+
+#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const))
+const char* const infrared_debug_cfg_variables_text[] = {
+ "Internal",
+ "2 (A7)",
+};
+
+static void infrared_scene_debug_settings_changed(VariableItem* item) {
+ Infrared* infrared = variable_item_get_context(item);
+ value_index_ir = variable_item_get_current_value_index(item);
+ UNUSED(infrared);
+
+ variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
+
+ furi_hal_infrared_set_debug_out(value_index_ir);
+}
+static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
+ Infrared* infrared = context;
+ view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
+}
+
+void infrared_scene_debug_settings_on_enter(void* context) {
+ Infrared* infrared = context;
+
+ VariableItemList* variable_item_list = infrared->variable_item_list;
+
+ value_index_ir = furi_hal_infrared_get_debug_out_status();
+ VariableItem* item = variable_item_list_add(
+ variable_item_list,
+ "Send signal to",
+ DEB_PINS_COUNT,
+ infrared_scene_debug_settings_changed,
+ infrared);
+
+ variable_item_list_set_enter_callback(
+ variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
+
+ variable_item_set_current_value_index(item, value_index_ir);
+ variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
+
+ view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList);
+}
+
+bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
+ Infrared* infrared = context;
+ UNUSED(infrared);
+ UNUSED(event);
+
+ return false;
+}
+
+void infrared_scene_debug_settings_on_exit(void* context) {
+ Infrared* infrared = context;
+ variable_item_list_reset(infrared->variable_item_list);
+}
diff --git a/applications/main/infrared/scenes/infrared_scene_start.c b/applications/main/infrared/scenes/infrared_scene_start.c
index 4d13d6e0e..0403e40f8 100644
--- a/applications/main/infrared/scenes/infrared_scene_start.c
+++ b/applications/main/infrared/scenes/infrared_scene_start.c
@@ -5,7 +5,8 @@ enum SubmenuIndex {
SubmenuIndexLearnNewRemote,
SubmenuIndexLearnNewRemoteRaw,
SubmenuIndexSavedRemotes,
- SubmenuIndexDebug
+ SubmenuIndexDebug,
+ SubmenuIndexDebugSettings
};
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -45,7 +46,17 @@ void infrared_scene_start_on_enter(void* context) {
infrared_scene_start_submenu_callback,
infrared);
submenu_add_item(
- submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);
+ submenu,
+ "Debug RX",
+ SubmenuIndexDebug,
+ infrared_scene_start_submenu_callback,
+ infrared);
+ submenu_add_item(
+ submenu,
+ "Debug Settings",
+ SubmenuIndexDebugSettings,
+ infrared_scene_start_submenu_callback,
+ infrared);
}
const uint32_t submenu_index =
@@ -85,6 +96,9 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(submenu_index == SubmenuIndexDebug) {
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
consumed = true;
+ } else if(submenu_index == SubmenuIndexDebugSettings) {
+ scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings);
+ consumed = true;
}
}
diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c
index 09b77b1f7..b6c1ac983 100644
--- a/applications/main/infrared/scenes/infrared_scene_universal.c
+++ b/applications/main/infrared/scenes/infrared_scene_universal.c
@@ -52,7 +52,8 @@ void infrared_scene_universal_on_enter(void* context) {
infrared_scene_universal_submenu_callback,
context);
- submenu_set_selected_item(submenu, 0);
+ submenu_set_selected_item(
+ submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal));
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
@@ -79,6 +80,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
consumed = true;
}
+ scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event);
}
return consumed;
diff --git a/applications/main/infrared/views/infrared_progress_view.h b/applications/main/infrared/views/infrared_progress_view.h
index e8f76ba1f..4f3c3a875 100644
--- a/applications/main/infrared/views/infrared_progress_view.h
+++ b/applications/main/infrared/views/infrared_progress_view.h
@@ -10,7 +10,7 @@
extern "C" {
#endif
-/** Anonumous instance */
+/** Anonymous instance */
typedef struct InfraredProgressView InfraredProgressView;
/** Callback for back button handling */
diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h
index 201caa4f6..72b061930 100644
--- a/applications/main/lfrfid/lfrfid_i.h
+++ b/applications/main/lfrfid/lfrfid_i.h
@@ -5,8 +5,10 @@
#include
#include
+#include
#include
#include
+#include
#include
#include
@@ -32,8 +34,6 @@
#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_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
index 888288a31..a950de708 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
@@ -16,7 +16,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
Popup* popup = app->popup;
char curr_buf[32] = {};
- //TODO: use .txt file in resourses for passwords.
+ //TODO: use .txt file in resources for passwords.
const uint32_t default_passwords[] = {
0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F,
0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752,
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
index b1130d191..efa319c1e 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
@@ -20,7 +20,7 @@ void lfrfid_scene_extra_actions_on_enter(void* context) {
submenu_add_item(
submenu,
- "Read ASK (Animal, FDX)",
+ "Read ASK (FDX,Regular)",
SubmenuIndexASK,
lfrfid_scene_extra_actions_submenu_callback,
app);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c
index f639f0ae1..ddac3e8ba 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c
@@ -7,7 +7,7 @@ void lfrfid_scene_retry_confirm_on_enter(void* context) {
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app);
widget_add_string_element(
- widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
+ widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
widget_add_string_element(
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
index 8e1c92dbb..2d83ba53b 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
@@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
+ scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
+ // Like in the other apps, explicitly save the scene state
+ // in each branch in case the user cancels loading a file.
+ scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved);
furi_string_set(app->file_path, LFRFID_APP_FOLDER);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
consumed = true;
} else if(event.event == SubmenuIndexAddManually) {
+ scene_manager_set_scene_state(
+ app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
consumed = true;
} else if(event.event == SubmenuIndexExtraActions) {
+ scene_manager_set_scene_state(
+ app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions);
scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
consumed = true;
}
- scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event);
}
return consumed;
diff --git a/applications/main/nfc/helpers/nfc_emv_parser.h b/applications/main/nfc/helpers/nfc_emv_parser.h
index abe57f470..c636ca77d 100644
--- a/applications/main/nfc/helpers/nfc_emv_parser.h
+++ b/applications/main/nfc/helpers/nfc_emv_parser.h
@@ -9,7 +9,7 @@
* @param aid - AID number array
* @param aid_len - AID length
* @param aid_name - string to keep AID name
- * @return - true if AID found, false otherwies
+ * @return - true if AID found, false otherwise
*/
bool nfc_emv_parser_get_aid_name(
Storage* storage,
@@ -21,7 +21,7 @@ bool nfc_emv_parser_get_aid_name(
* @param storage Storage instance
* @param country_code - ISO 3166 country code
* @param country_name - string to keep country name
- * @return - true if country found, false otherwies
+ * @return - true if country found, false otherwise
*/
bool nfc_emv_parser_get_country_name(
Storage* storage,
@@ -32,7 +32,7 @@ bool nfc_emv_parser_get_country_name(
* @param storage Storage instance
* @param currency_code - ISO 3166 currency code
* @param currency_name - string to keep currency name
- * @return - true if currency found, false otherwies
+ * @return - true if currency found, false otherwise
*/
bool nfc_emv_parser_get_currency_name(
Storage* storage,
diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c
index a6475ca68..23335e299 100644
--- a/applications/main/nfc/nfc_cli.c
+++ b/applications/main/nfc/nfc_cli.c
@@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) {
while(!cmd_exit) {
cmd_exit |= cli_cmd_interrupt_received(cli);
if(furi_hal_nfc_detect(&dev_data, 400)) {
- printf("found: %s ", nfc_get_dev_type(dev_data.type));
+ printf("Found: %s ", nfc_get_dev_type(dev_data.type));
printf("UID length: %d, UID:", dev_data.uid_len);
for(size_t i = 0; i < dev_data.uid_len; i++) {
printf("%02X", dev_data.uid[i]);
diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h
index 045080fea..ef257b76e 100644
--- a/applications/main/nfc/scenes/nfc_scene_config.h
+++ b/applications/main/nfc/scenes/nfc_scene_config.h
@@ -34,6 +34,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu)
ADD_SCENE(nfc, mf_desfire_data, MfDesfireData)
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess)
+ADD_SCENE(nfc, mf_classic_data, MfClassicData)
ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
new file mode 100644
index 000000000..dcb02d364
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
@@ -0,0 +1,106 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_data_on_enter(void* context) {
+ Nfc* nfc = context;
+ MfClassicType type = nfc->dev->dev_data.mf_classic_data.type;
+ MfClassicData* data = &nfc->dev->dev_data.mf_classic_data;
+ TextBox* text_box = nfc->text_box;
+
+ text_box_set_font(text_box, TextBoxFontHex);
+
+ int card_blocks = 0;
+ if(type == MfClassicType1k) {
+ card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4;
+ } else if(type == MfClassicType4k) {
+ // 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each
+ card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16;
+ } else if(type == MfClassicTypeMini) {
+ card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4;
+ }
+
+ int bytes_written = 0;
+ for(int block_num = 0; block_num < card_blocks; block_num++) {
+ bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
+ if(is_sec_trailer) {
+ uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
+ MfClassicSectorTrailer* sec_tr =
+ mf_classic_get_sector_trailer_by_sector(data, sector_num);
+ // Key A
+ for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) {
+ furi_string_cat_printf(
+ nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ // Access bytes
+ for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_block_read(data, block_num)) {
+ furi_string_cat_printf(
+ nfc->text_box_store,
+ "%02X%02X ",
+ sec_tr->access_bits[i],
+ sec_tr->access_bits[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ // Key B
+ for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) {
+ furi_string_cat_printf(
+ nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ } else {
+ // Write data block
+ for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_block_read(data, block_num)) {
+ furi_string_cat_printf(
+ nfc->text_box_store,
+ "%02X%02X ",
+ data->block[block_num].value[i],
+ data->block[block_num].value[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ }
+ }
+ text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store));
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
+}
+
+bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void nfc_scene_mf_classic_data_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clean view
+ text_box_reset(nfc->text_box);
+ furi_string_reset(nfc->text_box_store);
+}
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 5fbdabe30..67b2a8530 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c
@@ -25,7 +25,7 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) {
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
submenu_add_item(
submenu,
- "Detect reader",
+ "Detect Reader",
SubmenuIndexDetectReader,
nfc_scene_mf_classic_menu_submenu_callback,
nfc);
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 372da8860..d6cb48047 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
@@ -26,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
NfcProtocol protocol = dev_data->protocol;
uint8_t text_scroll_height = 0;
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) ||
- (protocol == NfcDeviceProtocolNfcV)) {
+ (protocol == NfcDeviceProtocolNfcV) || (protocol == NfcDeviceProtocolMifareClassic)) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
text_scroll_height = 52;
@@ -290,6 +290,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
} else if(protocol == NfcDeviceProtocolNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true;
+ } else if(protocol == NfcDeviceProtocolMifareClassic) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
+ consumed = true;
}
}
}
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 9b2a2188e..ee80ee768 100644
--- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c
@@ -46,6 +46,9 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed =
+ scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c
index 366582ea8..5f4f7985e 100644
--- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c
+++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c
@@ -14,7 +14,7 @@ void nfc_scene_retry_confirm_on_enter(void* context) {
dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
dialog_ex_set_text(
- dialog_ex, "All unsaved data will be\nlost!", 64, 25, AlignCenter, AlignTop);
+ dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback);
diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
index 616dc0ef7..dddaa47b3 100644
--- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
@@ -52,20 +52,20 @@ void nfc_scene_saved_menu_on_enter(void* context) {
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
submenu_add_item(
submenu,
- "Detect reader",
+ "Detect Reader",
SubmenuIndexDetectReader,
nfc_scene_saved_menu_submenu_callback,
nfc);
}
submenu_add_item(
submenu,
- "Write To Initial Card",
+ "Write to Initial Card",
SubmenuIndexWrite,
nfc_scene_saved_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
- "Update From Initial Card",
+ "Update from Initial Card",
SubmenuIndexUpdate,
nfc_scene_saved_menu_submenu_callback,
nfc);
@@ -76,13 +76,13 @@ void nfc_scene_saved_menu_on_enter(void* context) {
!mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
submenu_add_item(
submenu,
- "Unlock With Reader",
+ "Unlock with Reader",
SubmenuIndexMfUlUnlockByReader,
nfc_scene_saved_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
- "Unlock With Password",
+ "Unlock with Password",
SubmenuIndexMfUlUnlockByPassword,
nfc_scene_saved_menu_submenu_callback,
nfc);
@@ -153,6 +153,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
application_info_present = nfc_supported_card_verify_and_parse(dev_data);
}
+ FURI_LOG_I("nfc", "application_info_present: %d", application_info_present);
+
if(application_info_present) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
} else {
diff --git a/applications/main/sub_playlist/playlist_10px.png b/applications/main/sub_playlist/playlist_10px.png
deleted file mode 100644
index 94ad885c3..000000000
Binary files a/applications/main/sub_playlist/playlist_10px.png and /dev/null differ
diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c
index dbf34c970..b589ba5d5 100644
--- a/applications/main/subghz/helpers/subghz_chat.c
+++ b/applications/main/subghz/helpers/subghz_chat.c
@@ -9,7 +9,7 @@ struct SubGhzChatWorker {
SubGhzTxRxWorker* subghz_txrx;
volatile bool worker_running;
- volatile bool worker_stoping;
+ volatile bool worker_stopping;
FuriMessageQueue* event_queue;
uint32_t last_time_rx_data;
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h
index 350e68ee6..4475045ee 100644
--- a/applications/main/subghz/helpers/subghz_custom_event.h
+++ b/applications/main/subghz/helpers/subghz_custom_event.h
@@ -8,11 +8,14 @@ typedef enum {
//SubmenuIndex
SubmenuIndexFaacSLH_433,
SubmenuIndexFaacSLH_868,
- SubmenuIndexBFT,
+ SubmenuIndexBFTClone,
+ SubmenuIndexBFTMitto,
+ SubmenuIndexSomfyTelis,
SubmenuIndexPricenton,
SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit,
SubmenuIndexNiceFlorS_433_92,
+ SubmenuIndexNiceOne_433_92,
SubmenuIndexNiceSmilo_433_92,
SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit,
@@ -64,6 +67,7 @@ typedef enum {
SubGhzCustomEventViewReceiverBack,
SubGhzCustomEventViewReceiverOffDisplay,
SubGhzCustomEventViewReceiverUnlock,
+ SubGhzCustomEventViewReceiverDeleteItem,
SubGhzCustomEventViewReadRAWBack,
SubGhzCustomEventViewReadRAWIDLE,
diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
index 6452792a6..ab90547cb 100644
--- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
+++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
@@ -117,16 +117,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// First stage: coarse scan
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
- if(furi_hal_subghz_is_frequency_valid(
- subghz_setting_get_frequency(instance->setting, i)) &&
+ uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
+ if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
+ (current_frequency != 467750000) && (current_frequency != 464000000) &&
!((furi_hal_subghz.radio_type == SubGhzRadioExternal) &&
- (subghz_setting_get_frequency(instance->setting, i) >= 311900000 &&
- subghz_setting_get_frequency(instance->setting, i) <= 312200000))) {
+ ((current_frequency == 390000000) || (current_frequency == 312000000) ||
+ (current_frequency == 312100000) || (current_frequency == 312200000) ||
+ (current_frequency == 440175000)))) {
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
- frequency = cc1101_set_frequency(
- furi_hal_subghz.spi_bus_handle,
- subghz_setting_get_frequency(instance->setting, i));
+ frequency =
+ cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, current_frequency);
cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
do {
@@ -331,4 +332,4 @@ void subghz_frequency_analyzer_worker_set_trigger_level(
float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) {
return instance->trigger_level;
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h
index 7d63ba6b9..3c5982427 100644
--- a/applications/main/subghz/helpers/subghz_types.h
+++ b/applications/main/subghz/helpers/subghz_types.h
@@ -23,7 +23,7 @@ typedef enum {
/** SubGhzHopperState state */
typedef enum {
SubGhzHopperStateOFF,
- SubGhzHopperStateRunnig,
+ SubGhzHopperStateRunning,
SubGhzHopperStatePause,
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
@@ -54,6 +54,7 @@ typedef enum {
SubGhzLoadKeyStateOK,
SubGhzLoadKeyStateParseErr,
SubGhzLoadKeyStateOnlyRx,
+ SubGhzLoadKeyStateProtocolDescriptionErr,
} SubGhzLoadKeyState;
/** SubGhzLock */
diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
index 7d7a505cb..c58661332 100644
--- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
+++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
@@ -1,8 +1,10 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-uint8_t value_index;
-uint8_t value_index2;
+uint8_t value_index_exm;
+uint8_t value_index_dpin;
+uint8_t value_index_cnt;
+uint8_t value_index_pwr;
#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const))
const char* const radio_modules_variables_text[] = {
@@ -10,18 +12,34 @@ const char* const radio_modules_variables_text[] = {
"External",
};
+#define EXT_MOD_POWER_COUNT 2
+const char* const ext_mod_power_text[EXT_MOD_POWER_COUNT] = {
+ "ON",
+ "OFF",
+};
+
#define DEBUG_P_COUNT 2
const char* const debug_pin_text[DEBUG_P_COUNT] = {
"OFF",
- "A7",
+ "17(1W)",
+};
+
+#define DEBUG_COUNTER_COUNT 6
+const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
+ "+1",
+ "+2",
+ "+3",
+ "+4",
+ "+5",
+ "+10",
};
static void subghz_scene_ext_module_changed(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
- value_index = variable_item_get_current_value_index(item);
+ value_index_exm = variable_item_get_current_value_index(item);
UNUSED(subghz);
- variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
}
static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
@@ -37,31 +55,114 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
subghz->txrx->debug_pin_state = index == 1;
}
+static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, debug_counter_text[index]);
+
+ switch(index) {
+ case 0:
+ furi_hal_subghz_set_rolling_counter_mult(1);
+ break;
+ case 1:
+ furi_hal_subghz_set_rolling_counter_mult(2);
+ break;
+ case 2:
+ furi_hal_subghz_set_rolling_counter_mult(3);
+ break;
+ case 3:
+ furi_hal_subghz_set_rolling_counter_mult(4);
+ break;
+ case 4:
+ furi_hal_subghz_set_rolling_counter_mult(5);
+ break;
+ case 5:
+ furi_hal_subghz_set_rolling_counter_mult(10);
+ break;
+ default:
+ break;
+ }
+}
+
+static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) {
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, ext_mod_power_text[index]);
+
+ furi_hal_subghz_set_external_power_disable(index == 1);
+ if(index == 1) {
+ furi_hal_subghz_disable_ext_power();
+ } else {
+ furi_hal_subghz_enable_ext_power();
+ }
+}
+
void subghz_scene_ext_module_settings_on_enter(void* context) {
SubGhz* subghz = context;
VariableItemList* variable_item_list = subghz->variable_item_list;
- value_index = furi_hal_subghz.radio_type;
+ value_index_exm = furi_hal_subghz.radio_type;
VariableItem* item = variable_item_list_add(
variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz);
variable_item_list_set_enter_callback(
variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz);
- variable_item_set_current_value_index(item, value_index);
- variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+ variable_item_set_current_value_index(item, value_index_exm);
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
+
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "Ext Radio 5v",
+ EXT_MOD_POWER_COUNT,
+ subghz_scene_receiver_config_set_ext_mod_power,
+ subghz);
+ value_index_pwr = furi_hal_subghz_get_external_power_disable();
+ variable_item_set_current_value_index(item, value_index_pwr);
+ variable_item_set_current_value_text(item, ext_mod_power_text[value_index_pwr]);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
item = variable_item_list_add(
subghz->variable_item_list,
- "Debug Pin:",
+ "Debug Pin",
DEBUG_P_COUNT,
subghz_scene_receiver_config_set_debug_pin,
subghz);
- value_index2 = subghz->txrx->debug_pin_state;
- variable_item_set_current_value_index(item, value_index2);
- variable_item_set_current_value_text(item, debug_pin_text[value_index2]);
+ value_index_dpin = subghz->txrx->debug_pin_state;
+ variable_item_set_current_value_index(item, value_index_dpin);
+ variable_item_set_current_value_text(item, debug_pin_text[value_index_dpin]);
+
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "Counter incr.",
+ DEBUG_COUNTER_COUNT,
+ subghz_scene_receiver_config_set_debug_counter,
+ subghz);
+ switch(furi_hal_subghz_get_rolling_counter_mult()) {
+ case 1:
+ value_index_cnt = 0;
+ break;
+ case 2:
+ value_index_cnt = 1;
+ break;
+ case 3:
+ value_index_cnt = 2;
+ break;
+ case 4:
+ value_index_cnt = 3;
+ break;
+ case 5:
+ value_index_cnt = 4;
+ break;
+ case 10:
+ value_index_cnt = 5;
+ break;
+ default:
+ break;
+ }
+ variable_item_set_current_value_index(item, value_index_cnt);
+ variable_item_set_current_value_text(item, debug_counter_text[value_index_cnt]);
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
@@ -73,12 +174,14 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent
UNUSED(event);
// Set selected radio module
- furi_hal_subghz_set_radio_type(value_index);
+ furi_hal_subghz_set_radio_type(value_index_exm);
+
+ furi_hal_subghz_enable_ext_power();
// Check if module is present, if no -> show error
if(!furi_hal_subghz_check_radio()) {
- value_index = 0;
- furi_hal_subghz_set_radio_type(value_index);
+ value_index_exm = 0;
+ furi_hal_subghz_set_radio_type(value_index_exm);
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c
index 2cfe060c7..5c6b9cc39 100644
--- a/applications/main/subghz/scenes/subghz_scene_need_saving.c
+++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c
@@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) {
SubGhz* subghz = context;
widget_add_string_multiline_element(
- subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz menu?");
+ subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?");
widget_add_string_multiline_element(
subghz->widget,
64,
@@ -24,7 +24,7 @@ void subghz_scene_need_saving_on_enter(void* context) {
AlignCenter,
AlignCenter,
FontSecondary,
- "All unsaved will be\nlost.");
+ "All unsaved data\nwill be lost!");
widget_add_button_element(
subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz);
diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c
index e396527cc..1e3d6a9c0 100644
--- a/applications/main/subghz/scenes/subghz_scene_read_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c
@@ -368,7 +368,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
float rssi = furi_hal_subghz_get_rssi();
- if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) {
+ if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_THRESHOLD_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);
@@ -421,4 +421,4 @@ void subghz_scene_read_raw_on_exit(void* context) {
//filter restoration
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c
index e1ea08497..c0112199c 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver.c
@@ -204,6 +204,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
consumed = true;
break;
+ case SubGhzCustomEventViewReceiverDeleteItem:
+ subghz->txrx->idx_menu_chosen =
+ subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
+
+ subghz_history_delete_item(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
+ subghz_view_receiver_delete_element_callback(subghz->subghz_receiver);
+
+ subghz_scene_receiver_update_statusbar(subghz);
+ consumed = true;
+ break;
case SubGhzCustomEventViewReceiverConfig:
subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->idx_menu_chosen =
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
index af4c6aca3..ff51e2f4d 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
@@ -8,11 +8,11 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexBinRAW,
SubGhzSettingIndexSound,
SubGhzSettingIndexLock,
- SubGhzSettingIndexRAWThesholdRSSI,
+ SubGhzSettingIndexRAWThresholdRSSI,
};
#define RAW_THRESHOLD_RSSI_COUNT 11
-const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
+const char* const raw_threshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
"-----",
"-85.0",
"-80.0",
@@ -26,7 +26,7 @@ const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
"-40.0",
};
-const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
+const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
-90.0f,
-85.0f,
-80.0f,
@@ -47,7 +47,7 @@ const char* const hopping_text[HOPPING_COUNT] = {
};
const uint32_t hopping_value[HOPPING_COUNT] = {
SubGhzHopperStateOFF,
- SubGhzHopperStateRunnig,
+ SubGhzHopperStateRunning,
};
#define SPEAKER_COUNT 2
@@ -213,8 +213,8 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
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];
+ variable_item_set_current_value_text(item, raw_threshold_rssi_text[index]);
+ subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
}
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
@@ -320,9 +320,9 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_set_raw_threshold_rssi,
subghz);
value_index = value_index_float(
- subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT);
+ subghz->txrx->raw_threshold_rssi, raw_threshold_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]);
+ variable_item_set_current_value_text(item, raw_threshold_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 0b265cca2..90046eb9d 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
@@ -2,6 +2,9 @@
#include "../helpers/subghz_custom_event.h"
#include
#include
+#include
+#include
+#include
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
@@ -24,8 +27,9 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver,
subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
+
if(subghz->txrx->decoder_result) {
- // In this case flipper format was changed to short file content
+ //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result,
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
@@ -135,7 +139,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz,
subghz_history_get_raw_data(
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
}
@@ -148,7 +151,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
subghz->state_notifications = SubGhzNotificationStateRx;
} else {
@@ -175,7 +178,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
subghz->state_notifications = SubGhzNotificationStateRx;
}
@@ -233,6 +236,10 @@ void subghz_scene_receiver_info_on_exit(void* context) {
widget_reset(subghz->widget);
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c
index 609b4a71d..b9585ec36 100644
--- a/applications/main/subghz/scenes/subghz_scene_rpc.c
+++ b/applications/main/subghz/scenes/subghz_scene_rpc.c
@@ -1,6 +1,10 @@
#include "../subghz_i.h"
#include
#include
+#include
+#include
+#include
+
#include "xtreme/assets.h"
typedef enum {
@@ -110,6 +114,10 @@ void subghz_scene_rpc_on_exit(void* context) {
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
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 1d6f9d70b..2c48462ef 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
@@ -14,7 +14,7 @@ void subghz_scene_set_seed_bft_on_enter(void* context) {
SubGhz* subghz = context;
// Setup view
- // roguemaster don't steal!!!
+ // RogueMaster don't steal!!!
ByteInput* byte_input = subghz->byte_input;
byte_input_set_header_text(byte_input, "Enter SEED in hex");
byte_input_set_result_callback(
diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
index 8e955e4b7..55387a0a5 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
@@ -65,7 +65,7 @@ bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event)
seed,
"FAAC_SLH",
subghz->txrx->preset);
- // rogueemaster dont steal!
+ // RogueMaster dont steal!
uint8_t seed_data[sizeof(uint32_t)] = {0};
for(size_t i = 0; i < sizeof(uint32_t); i++) {
seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF;
diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c
index 95cb1ec03..bc76ee146 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_type.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_type.c
@@ -36,8 +36,9 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
stream_clean(fff_data_stream);
- if(!subghz_protocol_decoder_base_serialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset)) {
+ if(subghz_protocol_decoder_base_serialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) !=
+ SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
@@ -79,10 +80,22 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexFaacSLH_433,
subghz_scene_set_type_submenu_callback,
subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "BFT [Manual] 433MHz",
+ SubmenuIndexBFTClone,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
submenu_add_item(
subghz->submenu,
"BFT Mitto 433MHz",
- SubmenuIndexBFT,
+ SubmenuIndexBFTMitto,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "Somfy Telis 433MHz",
+ SubmenuIndexSomfyTelis,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
@@ -115,6 +128,12 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexNiceFlorS_433_92,
subghz_scene_set_type_submenu_callback,
subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "Nice One 433MHz",
+ SubmenuIndexNiceOne_433_92,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
submenu_add_item(
subghz->submenu,
"CAME 12bit 433MHz",
@@ -230,7 +249,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexFaacSLH_433:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac);
break;
- case SubmenuIndexBFT:
+ case SubmenuIndexBFTClone:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft);
break;
case SubmenuIndexPricenton:
@@ -306,11 +325,69 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
generated_protocol = true;
}
break;
+ case SubmenuIndexBFTMitto:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_keeloq_bft_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x000FFFFF,
+ 0x2,
+ 0x0002,
+ key & 0x000FFFFF,
+ "BFT",
+ subghz->txrx->preset);
+
+ uint8_t seed_data[sizeof(uint32_t)] = {0};
+ for(size_t i = 0; i < sizeof(uint32_t); i++) {
+ seed_data[sizeof(uint32_t) - i - 1] = ((key & 0x000FFFFF) >> i * 8) & 0xFF;
+ }
+
+ 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;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
+ case SubmenuIndexSomfyTelis:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_somfy_telis_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x00FFFFFF,
+ 0x2,
+ 0x0003,
+ subghz->txrx->preset);
+ generated_protocol = true;
+ } else {
+ generated_protocol = false;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
case SubmenuIndexDoorHan_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
@@ -358,8 +435,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexNiceFlorS_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_nice_flor_s_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
@@ -367,7 +443,32 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
key & 0x0FFFFFFF,
0x1,
0x0003,
- subghz->txrx->preset);
+ subghz->txrx->preset,
+ false);
+ generated_protocol = true;
+ } else {
+ generated_protocol = false;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
+ case SubmenuIndexNiceOne_433_92:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_nice_flor_s_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x0FFFFFFF,
+ 0x1,
+ 0x0003,
+ subghz->txrx->preset,
+ true);
generated_protocol = true;
} else {
generated_protocol = false;
@@ -382,8 +483,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexNiceSmilo_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c
index 69e6cbea7..fd31353e0 100644
--- a/applications/main/subghz/scenes/subghz_scene_start.c
+++ b/applications/main/subghz/scenes/subghz_scene_start.c
@@ -75,43 +75,46 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings);
return true;
-
- } else if(!furi_hal_subghz_check_radio()) {
- furi_string_set(subghz->error_str, "Please connect\nexternal radio");
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
- return true;
- } else if(event.event == SubmenuIndexReadRAW) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
- subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
- return true;
- } else if(event.event == SubmenuIndexRead) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
- return true;
- } else if(event.event == SubmenuIndexSaved) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
- return true;
} else if(event.event == SubmenuIndexAddManually) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType);
return true;
- } else if(event.event == SubmenuIndexFrequencyAnalyzer) {
- 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) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest);
- return true;
+ } else {
+ furi_hal_subghz_enable_ext_power();
+
+ if(!furi_hal_subghz_check_radio()) {
+ furi_string_set(subghz->error_str, "Please connect\nexternal radio");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
+ return true;
+ } else if(event.event == SubmenuIndexReadRAW) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
+ subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
+ return true;
+ } else if(event.event == SubmenuIndexRead) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
+ return true;
+ } else if(event.event == SubmenuIndexSaved) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
+ return true;
+ } else if(event.event == SubmenuIndexFrequencyAnalyzer) {
+ 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) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest);
+ return true;
+ }
}
}
return false;
diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c
index dff4f8384..34685a8e5 100644
--- a/applications/main/subghz/scenes/subghz_scene_transmitter.c
+++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c
@@ -2,7 +2,10 @@
#include "../views/transmitter.h"
#include
#include
+#include
#include
+#include
+#include
void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
furi_assert(context);
@@ -11,9 +14,8 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
}
bool subghz_scene_transmitter_update_data_show(void* context) {
- //ToDo Fix
SubGhz* subghz = context;
-
+ bool ret = false;
if(subghz->txrx->decoder_result) {
FuriString* key_str;
FuriString* frequency_str;
@@ -24,30 +26,29 @@ bool subghz_scene_transmitter_update_data_show(void* context) {
modulation_str = furi_string_alloc();
uint8_t show_button = 0;
- subghz_protocol_decoder_base_deserialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data);
- subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
+ if(subghz_protocol_decoder_base_deserialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) {
+ subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
- if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
- SubGhzProtocolFlag_Send) {
- show_button = 1;
+ if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
+ SubGhzProtocolFlag_Send) {
+ show_button = 1;
+ }
+
+ subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
+ subghz_view_transmitter_add_data_to_show(
+ subghz->subghz_transmitter,
+ furi_string_get_cstr(key_str),
+ furi_string_get_cstr(frequency_str),
+ furi_string_get_cstr(modulation_str),
+ show_button);
+ ret = true;
}
-
- subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
- subghz_view_transmitter_add_data_to_show(
- subghz->subghz_transmitter,
- furi_string_get_cstr(key_str),
- furi_string_get_cstr(frequency_str),
- furi_string_get_cstr(modulation_str),
- show_button);
-
furi_string_free(frequency_str);
furi_string_free(modulation_str);
furi_string_free(key_str);
-
- return true;
}
- return false;
+ return ret;
}
void subghz_scene_transmitter_on_enter(void* context) {
@@ -89,6 +90,27 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
+ if(keeloq_get_custom_btn() != 0) {
+ keeloq_set_btn(0);
+ alutech_set_btn(0);
+ nice_flors_set_btn(0);
+ somfy_telis_set_btn(0);
+ uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
+ furi_hal_subghz_set_rolling_counter_mult(0);
+ // Calling restore!
+ if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+ subghz_rx_end(subghz);
+ }
+ if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
+ (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
+ if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
+ }
+ }
+ subghz_tx_stop(subghz);
+ subghz_sleep(subghz);
+ furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
+ }
return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
@@ -113,6 +135,10 @@ void subghz_scene_transmitter_on_exit(void* context) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c
index 78295f08c..e2b484120 100644
--- a/applications/main/subghz/subghz.c
+++ b/applications/main/subghz/subghz.c
@@ -275,7 +275,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->raw_threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->fff_data = flipper_format_string_alloc();
@@ -421,6 +421,9 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) {
// The rest
free(subghz);
+
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
}
int32_t subghz_app(void* p) {
diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c
index 184146698..f6530238e 100644
--- a/applications/main/subghz/subghz_history.c
+++ b/applications/main/subghz/subghz_history.c
@@ -88,6 +88,28 @@ void subghz_history_reset(SubGhzHistory* instance) {
instance->code_last_hash_data = 0;
}
+void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) {
+ furi_assert(instance);
+
+ SubGhzHistoryItemArray_it_t it;
+ //SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id);
+ SubGhzHistoryItemArray_it_last(it, instance->history->data);
+ while(!SubGhzHistoryItemArray_end_p(it)) {
+ SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it);
+
+ if(it->index == (size_t)(item_id)) {
+ furi_string_free(item->item_str);
+ furi_string_free(item->preset->name);
+ free(item->preset);
+ flipper_format_free(item->flipper_string);
+ item->type = 0;
+ SubGhzHistoryItemArray_remove(instance->history->data, it);
+ }
+ SubGhzHistoryItemArray_previous(it);
+ }
+ instance->last_index_write--;
+}
+
uint16_t subghz_history_get_item(SubGhzHistory* instance) {
furi_assert(instance);
return instance->last_index_write;
diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h
index 607dbeae2..1f2f8d246 100644
--- a/applications/main/subghz/subghz_history.h
+++ b/applications/main/subghz/subghz_history.h
@@ -27,6 +27,8 @@ void subghz_history_free(SubGhzHistory* instance);
*/
void subghz_history_reset(SubGhzHistory* instance);
+void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id);
+
/** Get frequency to history[idx]
*
* @param instance - SubGhzHistory instance
@@ -80,7 +82,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
*
* @param instance - SubGhzHistory instance
* @param output - FuriString* output
- * @return bool - is FUUL
+ * @return bool - is FULL
*/
bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output);
diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h
index 7c554621b..12ce28fff 100644
--- a/applications/main/subghz/subghz_history_private.h
+++ b/applications/main/subghz/subghz_history_private.h
@@ -92,7 +92,7 @@ uint32_t subghz_history_rand_range(uint32_t min, uint32_t max);
*
* @param file - Stream*
* @param is_negative_start - first value is negative or positive?
- * @param current_position - 0 if started from begining
+ * @param current_position - 0 if started from beginning
* @param empty_line - add RAW_Data to this line
* @return true
* @return false
@@ -160,4 +160,4 @@ bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool str
* @return true
* @return false
*/
-bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
\ No newline at end of file
+bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c
index 1fbe662ed..579a16520 100644
--- a/applications/main/subghz/subghz_i.c
+++ b/applications/main/subghz/subghz_i.c
@@ -154,7 +154,6 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
- //ToDo FIX
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
@@ -164,7 +163,8 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
subghz->txrx->environment, furi_string_get_cstr(temp_str));
if(subghz->txrx->transmitter) {
- if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) {
+ if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) ==
+ SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) {
subghz_begin(
subghz,
@@ -187,7 +187,12 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, subghz->txrx->transmitter);
+ } else {
+ subghz_dialog_message_show_only_rx(subghz);
}
+ } else {
+ dialog_message_show_storage_error(
+ subghz->dialogs, "Error in protocol\nparameters\ndescription");
}
}
if(!ret) {
@@ -335,8 +340,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver, furi_string_get_cstr(temp_str));
if(subghz->txrx->decoder_result) {
- if(!subghz_protocol_decoder_base_deserialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data)) {
+ SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data);
+ if(status != SubGhzProtocolStatusOk) {
+ load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;
break;
}
} else {
@@ -357,6 +364,12 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile");
}
return false;
+ case SubGhzLoadKeyStateProtocolDescriptionErr:
+ if(show_dialog) {
+ dialog_message_show_storage_error(
+ subghz->dialogs, "Error in protocol\nparameters\ndescription");
+ }
+ return false;
case SubGhzLoadKeyStateOnlyRx:
if(show_dialog) {
@@ -575,7 +588,7 @@ void subghz_hopper_update(SubGhz* subghz) {
return;
}
} else {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
// Select next frequency
if(subghz->txrx->hopper_idx_frequency <
@@ -598,7 +611,7 @@ void subghz_hopper_update(SubGhz* subghz) {
void subghz_speaker_on(SubGhz* subghz) {
if(subghz->txrx->debug_pin_state) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio);
}
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
@@ -643,7 +656,7 @@ void subghz_speaker_mute(SubGhz* subghz) {
void subghz_speaker_unmute(SubGhz* subghz) {
if(subghz->txrx->debug_pin_state) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio);
}
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c
index fa3569245..2a432deb5 100644
--- a/applications/main/subghz/views/receiver.c
+++ b/applications/main/subghz/views/receiver.c
@@ -14,7 +14,7 @@
#define MENU_ITEMS 4u
#define UNLOCK_CNT 3
-#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct {
FuriString* item_str;
@@ -82,10 +82,10 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
instance->view,
SubGhzViewReceiverModel * model,
{
- if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0;
} else {
- model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
+ model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
}
},
true);
@@ -261,13 +261,23 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
if(model->history_item == 0) {
if(model->mode == SubGhzViewReceiverModeLive) {
- canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
+ canvas_draw_icon(
+ canvas,
+ 0,
+ 0,
+ furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 :
+ XTREME_ASSETS()->I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Scanning...");
//canvas_draw_line(canvas, 46, 51, 125, 51);
canvas_set_font(canvas, FontSecondary);
} else {
- canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
+ canvas_draw_icon(
+ canvas,
+ 0,
+ 0,
+ furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 :
+ XTREME_ASSETS()->I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Decoding...");
canvas_set_font(canvas, FontSecondary);
@@ -421,6 +431,34 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
+ } else if(event->key == InputKeyRight && event->type == InputTypeLong) {
+ with_view_model(
+ subghz_receiver->view,
+ SubGhzViewReceiverModel * model,
+ {
+ if(model->history_item != 0) {
+ SubGhzReceiverMenuItemArray_it_t it;
+ // SubGhzReceiverMenuItem* target_item =
+ // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx);
+ SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
+ while(!SubGhzReceiverMenuItemArray_end_p(it)) {
+ SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
+
+ if(it->index == (size_t)(model->idx)) {
+ furi_string_free(item->item_str);
+ item->type = 0;
+ SubGhzReceiverMenuItemArray_remove(model->history->data, it);
+ }
+
+ SubGhzReceiverMenuItemArray_previous(it);
+ }
+
+ // Callback
+ subghz_receiver->callback(
+ SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context);
+ }
+ },
+ true);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view,
@@ -534,12 +572,34 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
- uint32_t idx = 0;
+ uint16_t idx = 0;
with_view_model(
subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);
return idx;
}
+void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) {
+ furi_assert(subghz_receiver);
+
+ with_view_model(
+ subghz_receiver->view,
+ SubGhzViewReceiverModel * model,
+ {
+ if(model->history_item == 5) {
+ if(model->idx >= 2) {
+ model->idx = model->history_item - 1;
+ }
+ }
+ model->history_item--;
+
+ if(model->idx != 0) {
+ model->idx--;
+ }
+ },
+ true);
+ furi_delay_ms(200);
+}
+
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver);
with_view_model(
diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h
index 37eb473de..ad8c31eda 100644
--- a/applications/main/subghz/views/receiver.h
+++ b/applications/main/subghz/views/receiver.h
@@ -46,4 +46,6 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
+void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver);
+
void subghz_view_receiver_exit(void* context);
diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c
index ce2e34297..3b8c37d73 100644
--- a/applications/main/subghz/views/subghz_frequency_analyzer.c
+++ b/applications/main/subghz/views/subghz_frequency_analyzer.c
@@ -21,13 +21,12 @@
#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, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000,
- 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000,
- 433657070, 433889000, 433920000, 434075000, 434176948, 434390000, 434420000,
- 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000,
- 868400000, 868800000, 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, 350000000, 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,
diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c
index 7ba2f4434..fcc077efa 100644
--- a/applications/main/subghz/views/subghz_read_raw.c
+++ b/applications/main/subghz/views/subghz_read_raw.c
@@ -23,7 +23,7 @@ typedef struct {
FuriString* sample_write;
FuriString* file_name;
uint8_t* rssi_history;
- uint8_t rssi_curret;
+ uint8_t rssi_current;
bool rssi_history_end;
uint8_t ind_write;
uint8_t ind_sin;
@@ -62,17 +62,17 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra
furi_assert(instance);
uint8_t u_rssi = 0;
- if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
u_rssi = 0;
} else {
- u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
+ u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
}
with_view_model(
instance->view,
SubGhzReadRAWModel * model,
{
- model->rssi_curret = u_rssi;
+ model->rssi_current = u_rssi;
if(trace) {
model->rssi_history[model->ind_write++] = u_rssi;
} else {
@@ -206,10 +206,10 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
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);
+ canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_current);
if(model->ind_write > 3) {
canvas_draw_line(
- canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret);
+ canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_current);
for(uint8_t i = 13; i < 47; i += width * 2) {
canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width);
@@ -231,13 +231,13 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
47,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
- 47 - model->rssi_curret);
+ 47 - model->rssi_current);
canvas_draw_line(
canvas,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
47,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
- 47 - model->rssi_curret);
+ 47 - model->rssi_current);
for(uint8_t i = 13; i < 47; i += width * 2) {
canvas_draw_line(
@@ -266,9 +266,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod
uint8_t x = 118;
uint8_t y = 48;
- if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {
uint8_t x = 118;
- y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
+ y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
uint8_t width = 3;
for(uint8_t i = 0; i < x; i += width * 2) {
diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h
index 43afac427..9d63870d5 100644
--- a/applications/main/subghz/views/subghz_read_raw.h
+++ b/applications/main/subghz/views/subghz_read_raw.h
@@ -3,7 +3,7 @@
#include
#include "../helpers/subghz_custom_event.h"
-#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct SubGhzReadRAW SubGhzReadRAW;
diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c
index 102639924..e6ed16d81 100644
--- a/applications/main/subghz/views/transmitter.c
+++ b/applications/main/subghz/views/transmitter.c
@@ -4,6 +4,11 @@
#include
#include
+#include
+#include
+#include
+#include
+
struct SubGhzViewTransmitter {
View* view;
SubGhzViewTransmitterCallback callback;
@@ -15,6 +20,8 @@ typedef struct {
FuriString* preset_str;
FuriString* key_str;
uint8_t show_button;
+ FuriString* temp_button_id;
+ bool draw_temp_button;
} SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback(
@@ -89,6 +96,12 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
+ if(model->draw_temp_button) {
+ canvas_set_font(canvas, FontBatteryPercent);
+ canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id));
+ canvas_set_font(canvas, FontSecondary);
+ }
+
if(model->show_button) {
canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
subghz_view_transmitter_button_right(canvas, "Send");
@@ -108,7 +121,9 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str);
furi_string_reset(model->key_str);
+ furi_string_reset(model->temp_button_id);
model->show_button = 0;
+ model->draw_temp_button = false;
},
false);
return false;
@@ -125,6 +140,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
true);
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ model->draw_temp_button = false;
+ },
+ true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
@@ -134,6 +157,141 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
return true;
}
+ // Temp Buttons (UP)
+ if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
+ keeloq_set_btn(1);
+ alutech_set_btn(1);
+ nice_flors_set_btn(1);
+ somfy_telis_set_btn(1);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Down
+ if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) {
+ keeloq_set_btn(2);
+ alutech_set_btn(2);
+ nice_flors_set_btn(2);
+ somfy_telis_set_btn(2);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Left
+ if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) {
+ keeloq_set_btn(3);
+ alutech_set_btn(3);
+ nice_flors_set_btn(3);
+ somfy_telis_set_btn(3);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Right
+ if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) {
+ keeloq_set_btn(4);
+ alutech_set_btn(4);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+
return true;
}
@@ -166,6 +324,7 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() {
model->frequency_str = furi_string_alloc();
model->preset_str = furi_string_alloc();
model->key_str = furi_string_alloc();
+ model->temp_button_id = furi_string_alloc();
},
true);
return subghz_transmitter;
@@ -181,6 +340,7 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {
furi_string_free(model->frequency_str);
furi_string_free(model->preset_str);
furi_string_free(model->key_str);
+ furi_string_free(model->temp_button_id);
},
true);
view_free(subghz_transmitter->view);
diff --git a/applications/main/unirfremix/application.fam b/applications/main/subghz_remote/application.fam
similarity index 55%
rename from applications/main/unirfremix/application.fam
rename to applications/main/subghz_remote/application.fam
index fd3553947..e09f8500f 100644
--- a/applications/main/unirfremix/application.fam
+++ b/applications/main/subghz_remote/application.fam
@@ -1,14 +1,14 @@
App(
- appid="unirfremix",
+ appid="subghz_remote",
name="Sub-GHz Remote",
apptype=FlipperAppType.APP,
- entry_point="unirfremix_app",
- cdefines=["APP_UNIRFREMIX"],
+ entry_point="subghz_remote_app",
+ cdefines=["APP_SUBGHZREMOTE"],
requires=[
"gui",
"dialogs",
],
- icon="A_UniRFRemix_14",
+ icon="A_SubGHzRemote_14",
stack_size=4 * 1024,
order=11,
)
diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/subghz_remote/subghz_remote_app.c
similarity index 87%
rename from applications/main/unirfremix/unirfremix_app.c
rename to applications/main/subghz_remote/subghz_remote_app.c
index 2b12a12b2..941cdb889 100644
--- a/applications/main/unirfremix/unirfremix_app.c
+++ b/applications/main/subghz_remote/subghz_remote_app.c
@@ -19,11 +19,14 @@
#include
#include
#include
+#include
+#include
+#include
-#define UNIRFMAP_FOLDER "/ext/subghz/unirf"
-#define UNIRFMAP_EXTENSION ".txt"
+#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote"
+#define SUBREMOTEMAP_EXTENSION ".txt"
-#define TAG "UniRF Remix"
+#define TAG "SubGHzRemote"
typedef struct {
uint32_t frequency;
@@ -36,7 +39,7 @@ typedef struct {
size_t data_size;
SubGhzProtocolDecoderBase* decoder;
-} UniRFPreset;
+} SubRemotePreset;
typedef struct {
FuriMutex* model_mutex;
@@ -50,7 +53,7 @@ typedef struct {
SubGhzEnvironment* environment;
SubGhzReceiver* subghz_receiver;
NotificationApp* notification;
- UniRFPreset* txpreset;
+ SubRemotePreset* txpreset;
FuriString* up_file;
FuriString* down_file;
@@ -85,17 +88,17 @@ typedef struct {
bool tx_not_allowed;
FuriString* signal;
-} UniRFRemix;
+} SubGHzRemote;
-UniRFPreset* unirfremix_preset_alloc(void) {
- UniRFPreset* preset = malloc(sizeof(UniRFPreset));
+SubRemotePreset* subghz_remote_preset_alloc(void) {
+ SubRemotePreset* preset = malloc(sizeof(SubRemotePreset));
preset->name = furi_string_alloc();
preset->protocol = furi_string_alloc();
preset->repeat = 200;
return preset;
}
-void unirfremix_preset_free(UniRFPreset* preset) {
+void subghz_remote_preset_free(SubRemotePreset* preset) {
furi_string_free(preset->name);
furi_string_free(preset->protocol);
free(preset);
@@ -167,7 +170,7 @@ static void cfg_read_file_label(
* set error flag if missing map file
*/
-void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) {
+void subghz_remote_cfg_set_check(SubGHzRemote* app, FuriString* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
@@ -310,11 +313,11 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) {
}
}
-static void unirfremix_end_send(UniRFRemix* app) {
+static void subghz_remote_end_send(SubGHzRemote* app) {
app->processing = 0;
}
-bool unirfremix_set_preset(UniRFPreset* p, const char* preset) {
+bool subghz_remote_set_preset(SubRemotePreset* p, const char* preset) {
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
furi_string_set(p->name, "AM270");
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
@@ -334,8 +337,8 @@ bool unirfremix_set_preset(UniRFPreset* p, const char* preset) {
return true;
}
-bool unirfremix_key_load(
- UniRFPreset* preset,
+bool subghz_remote_key_load(
+ SubRemotePreset* preset,
FlipperFormat* fff_file,
FlipperFormat* fff_data,
SubGhzSetting* setting,
@@ -364,7 +367,7 @@ bool unirfremix_key_load(
FURI_LOG_W(TAG, "Could not read Preset. Defaulting to Ook650Async");
furi_string_set(temp_str, "FuriHalSubGhzPresetOok650Async");
}
- if(!unirfremix_set_preset(preset, furi_string_get_cstr(temp_str))) {
+ if(!subghz_remote_set_preset(preset, furi_string_get_cstr(temp_str))) {
FURI_LOG_E(TAG, "Could not set preset");
break;
}
@@ -399,7 +402,10 @@ bool unirfremix_key_load(
preset->decoder = subghz_receiver_search_decoder_base_by_name(
receiver, furi_string_get_cstr(preset->protocol));
if(preset->decoder) {
- if(!subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data)) {
+ SubGhzProtocolStatus status =
+ subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data);
+ if(status != SubGhzProtocolStatusOk) {
+ FURI_LOG_E(TAG, "Protocol deserialize failed, status = %d", status);
break;
}
} else {
@@ -416,7 +422,7 @@ bool unirfremix_key_load(
// method modified from subghz_i.c
// https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456
-bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) {
+bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) {
furi_assert(fff_file);
furi_assert(dev_file_name);
@@ -453,7 +459,7 @@ bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_f
return saved;
}
-void unirfremix_tx_stop(UniRFRemix* app) {
+void subghz_remote_tx_stop(SubGHzRemote* app) {
if(app->processing == 0) {
return;
}
@@ -477,10 +483,14 @@ void unirfremix_tx_stop(UniRFRemix* app) {
FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
if(proto && proto->type == SubGhzProtocolTypeDynamic) {
FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
- unirfremix_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
+ subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
@@ -490,22 +500,22 @@ void unirfremix_tx_stop(UniRFRemix* app) {
notification_message(app->notification, &sequence_blink_stop);
- unirfremix_preset_free(app->txpreset);
+ subghz_remote_preset_free(app->txpreset);
flipper_format_free(app->tx_fff_data);
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
}
-static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
+static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) {
//
bool res = false;
do {
if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) {
printf(
"In your settings, only reception on this frequency (%lu) is allowed,\r\n"
- "the actual operation of the unirf app is not possible\r\n ",
+ "the actual operation of the subghz remote app is not possible\r\n ",
app->txpreset->frequency);
app->tx_not_allowed = true;
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
break;
} else {
app->tx_not_allowed = false;
@@ -522,13 +532,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(app->txpreset->data);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(app->txpreset->frequency);
- furi_hal_gpio_write(&gpio_cc1101_g0, false);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
if(!furi_hal_subghz_tx()) {
FURI_LOG_E(TAG, "Sending not allowed");
@@ -546,14 +557,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
return res;
}
-static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char* path) {
+static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) {
FURI_LOG_D(TAG, "Sending: %s", path);
app->tx_file_path = path;
app->tx_fff_data = flipper_format_string_alloc();
- app->txpreset = unirfremix_preset_alloc();
+ app->txpreset = subghz_remote_preset_alloc();
// load settings/stream from .sub file
FlipperFormat* fff_file = flipper_format_file_alloc(storage);
@@ -563,7 +574,7 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char
FURI_LOG_E(TAG, "Could not open file %s", path);
break;
}
- if(!unirfremix_key_load(
+ if(!subghz_remote_key_load(
app->txpreset,
fff_file,
app->tx_fff_data,
@@ -581,25 +592,25 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char
return;
}
- unirfremix_send_sub(app, app->tx_fff_data);
+ subghz_remote_send_sub(app, app->tx_fff_data);
}
-static void unirfremix_process_signal(UniRFRemix* app, FuriString* signal) {
+static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) {
view_port_update(app->view_port);
FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal));
if(strlen(furi_string_get_cstr(signal)) > 12) {
Storage* storage = furi_record_open(RECORD_STORAGE);
- unirfremix_send_signal(app, storage, furi_string_get_cstr(signal));
+ subghz_remote_send_signal(app, storage, furi_string_get_cstr(signal));
furi_record_close(RECORD_STORAGE);
} else if(strlen(furi_string_get_cstr(signal)) < 10) {
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
- UniRFRemix* app = ctx;
+ SubGHzRemote* app = ctx;
furi_check(furi_mutex_acquire(app->model_mutex, FuriWaitForever) == FuriStatusOk);
//setup different canvas settings
@@ -639,10 +650,10 @@ static void render_callback(Canvas* canvas, void* ctx) {
//canvas_draw_str(canvas, 0, 40, "D: ");
//canvas_draw_str(canvas, 0, 50, "Ok: ");
- //PNGs are located in assets/icons/UniRFRemix before compiliation
+ //PNGs are located in assets/icons/SubGHzRemote before compilation
//Icons for Labels
- //canvas_draw_icon(canvas, 0, 0, &I_UniRFRemix_LeftAlignedButtons_9x64);
+ //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64);
canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
@@ -691,7 +702,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
//Repeat indicator
//canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:");
- //canvas_draw_icon(canvas, 115, 39, &I_UniRFRemix_Repeat_12x14);
+ //canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14);
//canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat));
}
@@ -699,14 +710,14 @@ static void render_callback(Canvas* canvas, void* ctx) {
}
static void input_callback(InputEvent* input_event, void* ctx) {
- UniRFRemix* app = ctx;
+ SubGHzRemote* app = ctx;
furi_message_queue_put(app->input_queue, input_event, 0);
}
-void unirfremix_subghz_alloc(UniRFRemix* app) {
+void subghz_remote_subghz_alloc(SubGHzRemote* app) {
// load subghz presets
app->setting = subghz_setting_alloc();
- subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt"));
+ subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
// load mfcodes
app->environment = subghz_environment_alloc();
@@ -722,8 +733,11 @@ void unirfremix_subghz_alloc(UniRFRemix* app) {
app->subghz_receiver = subghz_receiver_alloc_init(app->environment);
}
-UniRFRemix* unirfremix_alloc(void) {
- UniRFRemix* app = malloc(sizeof(UniRFRemix));
+SubGHzRemote* subghz_remote_alloc(void) {
+ SubGHzRemote* app = malloc(sizeof(SubGHzRemote));
+
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
furi_hal_power_suppress_charge_enter();
@@ -744,9 +758,12 @@ UniRFRemix* unirfremix_alloc(void) {
return app;
}
-void unirfremix_free(UniRFRemix* app, bool with_subghz) {
+void subghz_remote_free(SubGHzRemote* app, bool with_subghz) {
furi_hal_power_suppress_charge_exit();
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
+
furi_string_free(app->up_file);
furi_string_free(app->down_file);
furi_string_free(app->left_file);
@@ -778,9 +795,9 @@ void unirfremix_free(UniRFRemix* app, bool with_subghz) {
free(app);
}
-int32_t unirfremix_app(void* p) {
+int32_t subghz_remote_app(void* p) {
UNUSED(p);
- UniRFRemix* app = unirfremix_alloc();
+ SubGHzRemote* app = subghz_remote_alloc();
app->file_path = furi_string_alloc();
app->signal = furi_string_alloc();
@@ -795,33 +812,40 @@ int32_t unirfremix_app(void* p) {
app->file_result = 3;
Storage* storage = furi_record_open(RECORD_STORAGE);
- if(!storage_simply_mkdir(storage, UNIRFMAP_FOLDER)) {
- FURI_LOG_E(TAG, "Could not create folder %s", UNIRFMAP_FOLDER);
+ // Remove old pre-included files to avoid duplicates on migrate
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/CVS_Walgreens.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Gas_Sign_Edit.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Ridin_Dirty.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Tesla_Handicap.txt"));
+ storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREMOTEMAP_FOLDER);
+
+ if(!storage_simply_mkdir(storage, SUBREMOTEMAP_FOLDER)) {
+ FURI_LOG_E(TAG, "Could not create folder %s", SUBREMOTEMAP_FOLDER);
}
furi_record_close(RECORD_STORAGE);
- furi_string_set(app->file_path, UNIRFMAP_FOLDER);
+ furi_string_set(app->file_path, SUBREMOTEMAP_FOLDER);
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(&browser_options, UNIRFMAP_EXTENSION, &I_sub1_10px);
- browser_options.base_path = UNIRFMAP_FOLDER;
+ dialog_file_browser_set_basic_options(&browser_options, SUBREMOTEMAP_EXTENSION, &I_sub1_10px);
+ browser_options.base_path = SUBREMOTEMAP_FOLDER;
bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options);
furi_record_close(RECORD_DIALOGS);
if(!res) {
FURI_LOG_E(TAG, "No file selected");
- unirfremix_free(app, false);
+ subghz_remote_free(app, false);
return 255;
} else {
//check map and population variables
- unirfremix_cfg_set_check(app, app->file_path);
+ subghz_remote_cfg_set_check(app, app->file_path);
}
// init subghz stuff
- unirfremix_subghz_alloc(app);
+ subghz_remote_subghz_alloc(app);
bool exit_loop = false;
@@ -871,7 +895,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->up_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -889,7 +913,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->down_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -907,7 +931,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->right_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -925,7 +949,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->left_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -943,13 +967,13 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->ok_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
case InputKeyBack:
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
exit_loop = true;
break;
default:
@@ -986,7 +1010,7 @@ int32_t unirfremix_app(void* p) {
app->processing = 2;
- unirfremix_process_signal(app, app->signal);
+ subghz_remote_process_signal(app, app->signal);
}
if(exit_loop == true) {
@@ -1042,7 +1066,7 @@ int32_t unirfremix_app(void* p) {
}
// remove & free all stuff created by app
- unirfremix_free(app, true);
+ subghz_remote_free(app, true);
return 0;
}
diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c
index 66604d166..217733c94 100644
--- a/applications/main/u2f/u2f_data.c
+++ b/applications/main/u2f/u2f_data.c
@@ -178,7 +178,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
uint8_t key_slot = 0;
uint32_t version = 0;
- // Check if unique key exists in secure eclave and generate it if missing
+ // Check if unique key exists in secure eclave(typo?) and generate it if missing
if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
FuriString* filetype;
diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam
new file mode 100644
index 000000000..266350521
--- /dev/null
+++ b/applications/main/xtreme_app/application.fam
@@ -0,0 +1,16 @@
+App(
+ appid="xtreme_app",
+ name="Xtreme Settings",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="xtreme_app",
+ cdefines=["APP_XTREME"],
+ requires=[
+ "gui",
+ "dolphin",
+ "xtreme",
+ ],
+ stack_size=2 * 1024,
+ # icon="A_Xtreme_14",
+ order=90,
+ fap_category=".Main",
+)
diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.c b/applications/main/xtreme_app/scenes/xtreme_app_scene.c
similarity index 100%
rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.c
rename to applications/main/xtreme_app/scenes/xtreme_app_scene.c
diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.h b/applications/main/xtreme_app/scenes/xtreme_app_scene.h
similarity index 100%
rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.h
rename to applications/main/xtreme_app/scenes/xtreme_app_scene.h
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h
new file mode 100644
index 000000000..a7370f9b4
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h
@@ -0,0 +1,11 @@
+ADD_SCENE(xtreme_app, start, Start)
+ADD_SCENE(xtreme_app, graphics, Graphics)
+ADD_SCENE(xtreme_app, statusbar, Statusbar)
+ADD_SCENE(xtreme_app, protocols, Protocols)
+ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies)
+ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic)
+ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper)
+ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd)
+ADD_SCENE(xtreme_app, dolphin, Dolphin)
+ADD_SCENE(xtreme_app, misc, Misc)
+ADD_SCENE(xtreme_app, misc_rename, MiscRename)
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c
new file mode 100644
index 000000000..a157b269a
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c
@@ -0,0 +1,92 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexXpLevel,
+ VarItemListIndexButthurtTimer,
+};
+
+void xtreme_app_scene_dolphin_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_dolphin_xp_level_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->dolphin_level = variable_item_get_current_value_index(item) + 1;
+ char level_str[4];
+ snprintf(level_str, 4, "%i", app->dolphin_level);
+ variable_item_set_current_value_text(item, level_str);
+ app->save_level = true;
+}
+
+const char* const butthurt_timer_names[] =
+ {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"};
+const int32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] =
+ {-1, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800};
+static void xtreme_app_scene_dolphin_butthurt_timer_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, butthurt_timer_names[index]);
+ XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index];
+ app->save_settings = true;
+ app->require_reboot = true;
+}
+
+void xtreme_app_scene_dolphin_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+ uint8_t value_index;
+
+ char level_str[4];
+ snprintf(level_str, 4, "%i", app->dolphin_level);
+ item = variable_item_list_add(
+ var_item_list,
+ "XP Level",
+ DOLPHIN_LEVEL_COUNT + 1,
+ xtreme_app_scene_dolphin_xp_level_changed,
+ app);
+ variable_item_set_current_value_index(item, app->dolphin_level - 1);
+ variable_item_set_current_value_text(item, level_str);
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Butthurt Timer",
+ COUNT_OF(butthurt_timer_names),
+ xtreme_app_scene_dolphin_butthurt_timer_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, butthurt_timer_names[value_index]);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_dolphin_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneDolphin));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_dolphin_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneDolphin, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_dolphin_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c
new file mode 100644
index 000000000..99a302a44
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c
@@ -0,0 +1,146 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexAssetPack,
+ VarItemListIndexAnimSpeed,
+ VarItemListIndexCycleAnims,
+ VarItemListIndexUnlockAnims,
+};
+
+void xtreme_app_scene_graphics_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_graphics_asset_pack_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(
+ item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1));
+ strlcpy(
+ XTREME_SETTINGS()->asset_pack,
+ index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1),
+ MAX_PACK_NAME_LEN);
+ app->asset_pack = index;
+ app->save_settings = true;
+ app->require_reboot = true;
+}
+
+const char* const anim_speed_names[] =
+ {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"};
+const int32_t anim_speed_values[COUNT_OF(anim_speed_names)] =
+ {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300};
+static void xtreme_app_scene_graphics_anim_speed_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, anim_speed_names[index]);
+ XTREME_SETTINGS()->anim_speed = anim_speed_values[index];
+ app->save_settings = true;
+}
+
+const char* const cycle_anims_names[] = {
+ "OFF",
+ "Meta.txt",
+ "30 S",
+ "1 M",
+ "5 M",
+ "10 M",
+ "15 M",
+ "30 M",
+ "1 H",
+ "2 H",
+ "6 H",
+ "12 H",
+ "24 H"};
+const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] =
+ {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400};
+static void xtreme_app_scene_graphics_cycle_anims_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, cycle_anims_names[index]);
+ XTREME_SETTINGS()->cycle_anims = cycle_anims_values[index];
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_graphics_unlock_anims_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->unlock_anims = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_graphics_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+ uint8_t value_index;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Asset Pack",
+ asset_packs_size(app->asset_packs) + 1,
+ xtreme_app_scene_graphics_asset_pack_changed,
+ app);
+ variable_item_set_current_value_index(item, app->asset_pack);
+ variable_item_set_current_value_text(
+ item,
+ app->asset_pack == 0 ? "SFW" : *asset_packs_get(app->asset_packs, app->asset_pack - 1));
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Anim Speed",
+ COUNT_OF(anim_speed_names),
+ xtreme_app_scene_graphics_anim_speed_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->anim_speed, anim_speed_values, COUNT_OF(anim_speed_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, anim_speed_names[value_index]);
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Cycle Anims",
+ COUNT_OF(cycle_anims_names),
+ xtreme_app_scene_graphics_cycle_anims_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->cycle_anims, cycle_anims_values, COUNT_OF(cycle_anims_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, cycle_anims_names[value_index]);
+
+ item = variable_item_list_add(
+ var_item_list, "Unlock Anims", 2, xtreme_app_scene_graphics_unlock_anims_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->unlock_anims);
+ variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_graphics_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneGraphics));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_graphics_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneGraphics, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_graphics_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c
new file mode 100644
index 000000000..030256be0
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c
@@ -0,0 +1,96 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexSortDirsFirst,
+ VarItemListIndexChangeDeviceName,
+ VarItemListIndexExperimentalOptions,
+ VarItemListIndexDarkMode,
+ VarItemListIndexLeftHanded,
+};
+
+void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_misc_sort_dirs_first_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->sort_dirs_first = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_misc_dark_mode_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->dark_mode = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_misc_left_handed_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->left_handed = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_misc_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "Sort Dirs First", 2, xtreme_app_scene_misc_sort_dirs_first_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first);
+ variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, " Experimental Options:", 0, NULL, app);
+
+ item = variable_item_list_add(
+ var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_dark_mode_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->dark_mode);
+ variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Left Handed", 2, xtreme_app_scene_misc_left_handed_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->left_handed);
+ variable_item_set_current_value_text(item, xtreme_settings->left_handed ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_misc_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMisc));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexChangeDeviceName:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_misc_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c
new file mode 100644
index 000000000..a5f5dd8c5
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c
@@ -0,0 +1,53 @@
+#include "../xtreme_app.h"
+
+enum TextInputIndex {
+ TextInputResultOk,
+};
+
+static void xtreme_app_scene_misc_rename_text_input_callback(void* context) {
+ XtremeApp* app = context;
+
+ app->save_name = true;
+ app->require_reboot = true;
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
+}
+
+void xtreme_app_scene_misc_rename_on_enter(void* context) {
+ XtremeApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "Leave empty for default");
+
+ text_input_set_result_callback(
+ text_input,
+ xtreme_app_scene_misc_rename_text_input_callback,
+ app,
+ app->device_name,
+ NAMECHANGER_TEXT_STORE_SIZE,
+ true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput);
+}
+
+bool xtreme_app_scene_misc_rename_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ switch(event.event) {
+ case TextInputResultOk:
+ scene_manager_previous_scene(app->scene_manager);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_misc_rename_on_exit(void* context) {
+ XtremeApp* app = context;
+ text_input_reset(app->text_input);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c
new file mode 100644
index 000000000..eb8285e34
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexBadkbMode,
+ VarItemListIndexBadbtRemember,
+ VarItemListIndexSubghzFrequencies,
+ VarItemListIndexSubghzExtend,
+ VarItemListIndexSubghzBypass,
+};
+
+void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "BT" : "USB");
+ XTREME_SETTINGS()->bad_bt = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bad_bt_remember = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_protocols_subghz_extend_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_extend = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
+ app->save_subghz = true;
+}
+
+static void xtreme_app_scene_protocols_subghz_bypass_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_bypass = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
+ app->save_subghz = true;
+}
+
+void xtreme_app_scene_protocols_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bad_bt);
+ variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB");
+
+ item = variable_item_list_add(
+ var_item_list, "BadBT Rmembr", 2, xtreme_app_scene_protocols_badbt_remember_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember);
+ variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app);
+
+ item = variable_item_list_add(
+ var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app);
+ variable_item_set_current_value_index(item, app->subghz_extend);
+ variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_protocols_subghz_bypass_changed, app);
+ variable_item_set_current_value_index(item, app->subghz_bypass);
+ variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocols));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexSubghzFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c
new file mode 100644
index 000000000..6b92d1eb0
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c
@@ -0,0 +1,76 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexUseDefaults,
+ VarItemListIndexStaticFrequencies,
+ VarItemListIndexHopperFrequencies,
+};
+
+void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ app->subghz_use_defaults = value;
+ app->save_subghz_frequencies = true;
+}
+
+void xtreme_app_scene_protocols_frequencies_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Use Defaults",
+ 2,
+ xtreme_app_scene_protocols_frequencies_use_defaults_changed,
+ app);
+ variable_item_set_current_value_index(item, app->subghz_use_defaults);
+ variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexStaticFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic);
+ break;
+ case VarItemListIndexHopperFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c
new file mode 100644
index 000000000..21b29e295
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c
@@ -0,0 +1,83 @@
+#include "../xtreme_app.h"
+
+enum TextInputResult {
+ TextInputResultOk,
+ TextInputResultError,
+};
+
+static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) {
+ XtremeApp* app = context;
+
+ char* end;
+ uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000;
+ if(*end || !furi_hal_subghz_is_frequency_valid(value)) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError);
+ return;
+ }
+ bool is_hopper =
+ scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ if(is_hopper) {
+ FrequencyList_push_back(app->subghz_hopper_frequencies, value);
+ } else {
+ FrequencyList_push_back(app->subghz_static_frequencies, value);
+ }
+ app->save_subghz_frequencies = true;
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
+}
+
+void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) {
+ XtremeApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "Format: 12356");
+
+ strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE);
+
+ text_input_set_result_callback(
+ text_input,
+ xtreme_app_scene_protocols_frequencies_add_text_input_callback,
+ app,
+ app->subghz_freq_buffer,
+ XTREME_SUBGHZ_FREQ_BUFFER_SIZE,
+ true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput);
+}
+
+void callback_return(void* context) {
+ XtremeApp* app = context;
+ scene_manager_previous_scene(app->scene_manager);
+}
+
+bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ switch(event.event) {
+ case TextInputResultOk:
+ scene_manager_previous_scene(app->scene_manager);
+ break;
+ case TextInputResultError:
+ popup_set_header(app->popup, "Invalid value!", 64, 26, AlignCenter, AlignCenter);
+ popup_set_text(
+ app->popup, "Frequency was not added...", 64, 40, AlignCenter, AlignCenter);
+ popup_set_callback(app->popup, callback_return);
+ popup_set_context(app->popup, app);
+ popup_set_timeout(app->popup, 1000);
+ popup_enable_timeout(app->popup);
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) {
+ XtremeApp* app = context;
+ text_input_reset(app->text_input);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c
new file mode 100644
index 000000000..24c7fe3cc
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexHopperFrequency,
+ VarItemListIndexDeleteHopperFreq,
+ VarItemListIndexAddHopperFreq,
+};
+
+void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback(
+ void* context,
+ uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_hopper_index = variable_item_get_current_value_index(item);
+ uint32_t value = *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+}
+
+void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Hopper Freq",
+ FrequencyList_size(app->subghz_hopper_frequencies),
+ xtreme_app_scene_protocols_frequencies_hopper_frequency_changed,
+ app);
+ app->subghz_hopper_index = 0;
+ variable_item_set_current_value_index(item, app->subghz_hopper_index);
+ if(FrequencyList_size(app->subghz_hopper_frequencies)) {
+ uint32_t value =
+ *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+ } else {
+ variable_item_set_current_value_text(item, "None");
+ }
+
+ variable_item_list_add(var_item_list, "Delete Hopper Freq", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexDeleteHopperFreq:
+ if(!FrequencyList_size(app->subghz_hopper_frequencies)) break;
+ uint32_t value =
+ *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ FrequencyList_it_t it;
+ FrequencyList_it(it, app->subghz_hopper_frequencies);
+ while(!FrequencyList_end_p(it)) {
+ if(*FrequencyList_ref(it) == value) {
+ FrequencyList_remove(app->subghz_hopper_frequencies, it);
+ } else {
+ FrequencyList_next(it);
+ }
+ }
+ app->save_subghz_frequencies = true;
+ scene_manager_previous_scene(app->scene_manager);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper);
+ break;
+ case VarItemListIndexAddHopperFreq:
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c
new file mode 100644
index 000000000..4d70a1a58
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexStaticFrequency,
+ VarItemListIndexDeleteStaticFreq,
+ VarItemListIndexAddStaticFreq,
+};
+
+void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback(
+ void* context,
+ uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_static_index = variable_item_get_current_value_index(item);
+ uint32_t value = *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+}
+
+void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Static Freq",
+ FrequencyList_size(app->subghz_static_frequencies),
+ xtreme_app_scene_protocols_frequencies_static_frequency_changed,
+ app);
+ app->subghz_static_index = 0;
+ variable_item_set_current_value_index(item, app->subghz_static_index);
+ if(FrequencyList_size(app->subghz_static_frequencies)) {
+ uint32_t value =
+ *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+ } else {
+ variable_item_set_current_value_text(item, "None");
+ }
+
+ variable_item_list_add(var_item_list, "Delete Static Freq", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexDeleteStaticFreq:
+ if(!FrequencyList_size(app->subghz_static_frequencies)) break;
+ uint32_t value =
+ *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ FrequencyList_it_t it;
+ FrequencyList_it(it, app->subghz_static_frequencies);
+ while(!FrequencyList_end_p(it)) {
+ if(*FrequencyList_ref(it) == value) {
+ FrequencyList_remove(app->subghz_static_frequencies, it);
+ } else {
+ FrequencyList_next(it);
+ }
+ }
+ app->save_subghz_frequencies = true;
+ scene_manager_previous_scene(app->scene_manager);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic);
+ break;
+ case VarItemListIndexAddStaticFreq:
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c
new file mode 100644
index 000000000..c1c7659fc
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c
@@ -0,0 +1,71 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexGraphics,
+ VarItemListIndexStatusbar,
+ VarItemListIndexProtocols,
+ VarItemListIndexDolphin,
+ VarItemListIndexMisc,
+};
+
+void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void xtreme_app_scene_start_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+
+ variable_item_list_add(var_item_list, "Graphics", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Protocols", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Misc", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_start_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStart));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStart, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexGraphics:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneGraphics);
+ break;
+ case VarItemListIndexStatusbar:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneStatusbar);
+ break;
+ case VarItemListIndexProtocols:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocols);
+ break;
+ case VarItemListIndexDolphin:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneDolphin);
+ break;
+ case VarItemListIndexMisc:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_start_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c
new file mode 100644
index 000000000..48d7e6484
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c
@@ -0,0 +1,107 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexBatteryIcon,
+ VarItemListIndexStatusIcons,
+ VarItemListIndexBarBorders,
+ VarItemListIndexbarBackground,
+};
+
+void xtreme_app_scene_statusbar_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+const char* const battery_icon_names[] =
+ {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"};
+static void xtreme_app_scene_statusbar_battery_icon_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, battery_icon_names[index]);
+ XTREME_SETTINGS()->battery_icon = index;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_status_icons_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->status_icons = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_bar_borders_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bar_borders = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_bar_background_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bar_background = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_statusbar_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Battery Icon",
+ BatteryIconCount,
+ xtreme_app_scene_statusbar_battery_icon_changed,
+ app);
+ variable_item_set_current_value_index(item, xtreme_settings->battery_icon);
+ variable_item_set_current_value_text(item, battery_icon_names[xtreme_settings->battery_icon]);
+
+ item = variable_item_list_add(
+ var_item_list, "Status Icons", 2, xtreme_app_scene_statusbar_status_icons_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->status_icons);
+ variable_item_set_current_value_text(item, xtreme_settings->status_icons ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Bar Borders", 2, xtreme_app_scene_statusbar_bar_borders_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bar_borders);
+ variable_item_set_current_value_text(item, xtreme_settings->bar_borders ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Bar Background", 2, xtreme_app_scene_statusbar_bar_background_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bar_background);
+ variable_item_set_current_value_text(item, xtreme_settings->bar_background ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_statusbar_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStatusbar));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_statusbar_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStatusbar, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_statusbar_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c
new file mode 100644
index 000000000..fa66a8d52
--- /dev/null
+++ b/applications/main/xtreme_app/xtreme_app.c
@@ -0,0 +1,274 @@
+#include "xtreme_app.h"
+
+static bool xtreme_app_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ XtremeApp* app = context;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+void callback_reboot(void* context) {
+ UNUSED(context);
+ power_reboot(PowerBootModeNormal);
+}
+
+static bool xtreme_app_back_event_callback(void* context) {
+ furi_assert(context);
+ XtremeApp* app = context;
+
+ if(!scene_manager_has_previous_scene(app->scene_manager, XtremeAppSceneStart)) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ if(app->save_subghz) {
+ furi_hal_subghz_set_extend_settings(app->subghz_extend, app->subghz_bypass);
+ }
+
+ if(app->save_subghz_frequencies) {
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+ do {
+ FrequencyList_it_t it;
+ if(!flipper_format_file_open_always(file, EXT_PATH("subghz/assets/setting_user")))
+ break;
+
+ if(!flipper_format_write_header_cstr(
+ file, SUBGHZ_SETTING_FILE_TYPE, SUBGHZ_SETTING_FILE_VERSION))
+ break;
+
+ while(flipper_format_delete_key(file, "Add_standard_frequencies"))
+ ;
+ flipper_format_write_bool(
+ file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_delete_key(file, "Frequency"))
+ ;
+ FrequencyList_it(it, app->subghz_static_frequencies);
+ for(uint i = 0; i < FrequencyList_size(app->subghz_static_frequencies); i++) {
+ flipper_format_write_uint32(
+ file, "Frequency", FrequencyList_get(app->subghz_static_frequencies, i), 1);
+ }
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_delete_key(file, "Hopper_frequency"))
+ ;
+ for(uint i = 0; i < FrequencyList_size(app->subghz_hopper_frequencies); i++) {
+ flipper_format_write_uint32(
+ file,
+ "Hopper_frequency",
+ FrequencyList_get(app->subghz_hopper_frequencies, i),
+ 1);
+ }
+ } while(false);
+ flipper_format_free(file);
+ }
+
+ if(app->save_level) {
+ Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
+ int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0;
+ dolphin->state->data.icounter = xp + 1;
+ dolphin->state->dirty = true;
+ dolphin_state_save(dolphin->state);
+ furi_record_close(RECORD_DOLPHIN);
+ }
+
+ if(app->save_name) {
+ if(strcmp(app->device_name, "") == 0) {
+ storage_simply_remove(storage, NAMECHANGER_PATH);
+ } else {
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+
+ do {
+ if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) break;
+
+ if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, 1)) break;
+
+ if(!flipper_format_write_comment_cstr(
+ file,
+ "Changing the value below will change your FlipperZero device name."))
+ break;
+ if(!flipper_format_write_comment_cstr(
+ file,
+ "Note: This is limited to 8 characters using the following: a-z, A-Z, 0-9, and _"))
+ break;
+ if(!flipper_format_write_comment_cstr(
+ file, "It cannot contain any other characters."))
+ break;
+
+ if(!flipper_format_write_string_cstr(file, "Name", app->device_name)) break;
+
+ } while(0);
+
+ flipper_format_free(file);
+ }
+ }
+
+ if(app->save_settings) {
+ XTREME_SETTINGS_SAVE();
+ }
+
+ if(app->require_reboot) {
+ popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter);
+ popup_set_text(app->popup, "Applying changes...", 64, 40, AlignCenter, AlignCenter);
+ popup_set_callback(app->popup, callback_reboot);
+ popup_set_context(app->popup, app);
+ popup_set_timeout(app->popup, 1000);
+ popup_enable_timeout(app->popup);
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup);
+ return true;
+ }
+
+ furi_record_close(RECORD_STORAGE);
+ }
+
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+XtremeApp* xtreme_app_alloc() {
+ XtremeApp* app = malloc(sizeof(XtremeApp));
+ app->gui = furi_record_open(RECORD_GUI);
+
+ // View Dispatcher and Scene Manager
+ app->view_dispatcher = view_dispatcher_alloc();
+ app->scene_manager = scene_manager_alloc(&xtreme_app_scene_handlers, app);
+ view_dispatcher_enable_queue(app->view_dispatcher);
+ view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+
+ view_dispatcher_set_custom_event_callback(
+ app->view_dispatcher, xtreme_app_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, xtreme_app_back_event_callback);
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ // Gui Modules
+ app->var_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ XtremeAppViewVarItemList,
+ variable_item_list_get_view(app->var_item_list));
+
+ app->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, XtremeAppViewTextInput, text_input_get_view(app->text_input));
+
+ app->popup = popup_alloc();
+ view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup));
+
+ // Settings init
+
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+ FrequencyList_init(app->subghz_static_frequencies);
+ FrequencyList_init(app->subghz_hopper_frequencies);
+ app->subghz_use_defaults = true;
+ do {
+ uint32_t temp;
+ if(!flipper_format_file_open_existing(file, EXT_PATH("subghz/assets/setting_user"))) break;
+
+ flipper_format_read_bool(file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_read_uint32(file, "Frequency", &temp, 1)) {
+ if(furi_hal_subghz_is_frequency_valid(temp)) {
+ FrequencyList_push_back(app->subghz_static_frequencies, temp);
+ }
+ }
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_read_uint32(file, "Hopper_frequency", &temp, 1)) {
+ if(furi_hal_subghz_is_frequency_valid(temp)) {
+ FrequencyList_push_back(app->subghz_hopper_frequencies, temp);
+ }
+ }
+ } while(false);
+ flipper_format_free(file);
+
+ furi_hal_subghz_get_extend_settings(&app->subghz_extend, &app->subghz_bypass);
+
+ Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
+ DolphinStats stats = dolphin_stats(dolphin);
+ app->dolphin_level = stats.level;
+ furi_record_close(RECORD_DOLPHIN);
+
+ strlcpy(app->device_name, furi_hal_version_get_name_ptr(), NAMECHANGER_TEXT_STORE_SIZE);
+
+ app->asset_pack = 0;
+ asset_packs_init(app->asset_packs);
+ File* folder = storage_file_alloc(storage);
+ FileInfo info;
+ char* name = malloc(MAX_PACK_NAME_LEN);
+ if(storage_dir_open(folder, PACKS_DIR)) {
+ while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) {
+ if(info.flags & FSF_DIRECTORY) {
+ char* copy = malloc(MAX_PACK_NAME_LEN);
+ strlcpy(copy, name, MAX_PACK_NAME_LEN);
+ uint idx = 0;
+ if(strcmp(copy, "NSFW") != 0) {
+ for(; idx < asset_packs_size(app->asset_packs); idx++) {
+ char* comp = *asset_packs_get(app->asset_packs, idx);
+ if(strcasecmp(copy, comp) < 0 && strcmp(comp, "NSFW") != 0) {
+ break;
+ }
+ }
+ }
+ asset_packs_push_at(app->asset_packs, idx, copy);
+ if(app->asset_pack != 0) {
+ if(idx < app->asset_pack) app->asset_pack++;
+ } else {
+ if(strcmp(copy, xtreme_settings->asset_pack) == 0) app->asset_pack = idx + 1;
+ }
+ }
+ }
+ }
+ free(name);
+ storage_file_free(folder);
+ furi_record_close(RECORD_STORAGE);
+
+ app->version_tag =
+ furi_string_alloc_printf("%s %s", version_get_version(NULL), version_get_builddate(NULL));
+
+ return app;
+}
+
+void xtreme_app_free(XtremeApp* app) {
+ furi_assert(app);
+
+ // Gui modules
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewVarItemList);
+ variable_item_list_free(app->var_item_list);
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewTextInput);
+ text_input_free(app->text_input);
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup);
+ popup_free(app->popup);
+
+ // View Dispatcher and Scene Manager
+ view_dispatcher_free(app->view_dispatcher);
+ scene_manager_free(app->scene_manager);
+
+ // Settings deinit
+
+ FrequencyList_clear(app->subghz_static_frequencies);
+ FrequencyList_clear(app->subghz_hopper_frequencies);
+
+ asset_packs_it_t it;
+ for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) {
+ free(*asset_packs_cref(it));
+ }
+ asset_packs_clear(app->asset_packs);
+
+ furi_string_free(app->version_tag);
+
+ // Records
+ furi_record_close(RECORD_GUI);
+ free(app);
+}
+
+extern int32_t xtreme_app(void* p) {
+ UNUSED(p);
+ XtremeApp* app = xtreme_app_alloc();
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneStart);
+ view_dispatcher_run(app->view_dispatcher);
+ xtreme_app_free(app);
+ return 0;
+}
diff --git a/applications/settings/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h
similarity index 54%
rename from applications/settings/xtreme_app/xtreme_app.h
rename to applications/main/xtreme_app/xtreme_app.h
index 23adbf5ac..d3af90b6e 100644
--- a/applications/settings/xtreme_app/xtreme_app.h
+++ b/applications/main/xtreme_app/xtreme_app.h
@@ -7,16 +7,22 @@
#include
#include
#include
+#include
#include
+#include
+#include
#include "scenes/xtreme_app_scene.h"
#include "dolphin/helpers/dolphin_state.h"
#include "dolphin/dolphin.h"
#include "dolphin/dolphin_i.h"
#include
+#include
#include
#include "xtreme/settings.h"
#include "xtreme/assets.h"
+#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6
+
ARRAY_DEF(asset_packs, char*)
typedef struct {
@@ -24,18 +30,31 @@ typedef struct {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
VariableItemList* var_item_list;
+ TextInput* text_input;
Popup* popup;
- int dolphin_level;
+ bool subghz_use_defaults;
+ FrequencyList_t subghz_static_frequencies;
+ uint8_t subghz_static_index;
+ FrequencyList_t subghz_hopper_frequencies;
+ uint8_t subghz_hopper_index;
+ char subghz_freq_buffer[XTREME_SUBGHZ_FREQ_BUFFER_SIZE];
bool subghz_extend;
bool subghz_bypass;
- bool settings_changed;
- bool assets_changed;
- bool subghz_changed;
- bool level_changed;
+ int dolphin_level;
+ char device_name[NAMECHANGER_TEXT_STORE_SIZE];
+ uint asset_pack;
asset_packs_t asset_packs;
+ FuriString* version_tag;
+ bool save_subghz;
+ bool save_subghz_frequencies;
+ bool save_level;
+ bool save_name;
+ bool save_settings;
+ bool require_reboot;
} XtremeApp;
typedef enum {
XtremeAppViewVarItemList,
+ XtremeAppViewTextInput,
XtremeAppViewPopup,
} XtremeAppView;
diff --git a/applications/plugins/airmouse/README.md b/applications/plugins/airmouse/README.md
index 9df0d69b0..04e346e4b 100644
--- a/applications/plugins/airmouse/README.md
+++ b/applications/plugins/airmouse/README.md
@@ -19,6 +19,7 @@ Using it is really simple:
* Up button for Left mouse click;
* Down button for Right mouse click;
* Center button for Middle mouse click;
+ * Left and Right buttons for scrolling;
* Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done).
See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA).
@@ -41,10 +42,9 @@ Reality:

-
## Software
-The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project
+The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the Google [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project
If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following:
```
@@ -54,3 +54,7 @@ cd ../..
./fbt fap_air_mouse
```
If you're not familiar with those, just grab a `fap` file from Releases.
+
+## License
+
+TL;DR: Use the code however you want, give credit where it's due, no warranty of any kind is provided.
diff --git a/applications/plugins/airmouse/application.fam b/applications/plugins/airmouse/application.fam
index c646ee7e1..abc3f55bb 100644
--- a/applications/plugins/airmouse/application.fam
+++ b/applications/plugins/airmouse/application.fam
@@ -1,5 +1,5 @@
App(
- appid="BMI160_Air_Mouse",
+ appid="Air_Mouse",
name="[BMI160] Air Mouse",
apptype=FlipperAppType.EXTERNAL,
entry_point="air_mouse_app",
diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/plugins/airmouse/views/bt_mouse.c
index e6e0ae45a..7d9c0e6db 100644
--- a/applications/plugins/airmouse/views/bt_mouse.c
+++ b/applications/plugins/airmouse/views/bt_mouse.c
@@ -44,8 +44,7 @@ struct BtMouse {
#define BT_MOUSE_FLAG_KILL_THREAD (1UL << 1)
#define BT_MOUSE_FLAG_ALL (BT_MOUSE_FLAG_INPUT_EVENT | BT_MOUSE_FLAG_KILL_THREAD)
-#define MOUSE_MOVE_SHORT 5
-#define MOUSE_MOVE_LONG 20
+#define MOUSE_SCROLL 2
static void bt_mouse_notify_event(BtMouse* bt_mouse) {
FuriThreadId thread_id = furi_thread_get_id(bt_mouse->thread);
@@ -100,6 +99,14 @@ static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) {
} else if(event->type == InputTypeRelease) {
bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_WHEEL, false);
}
+ } else if(event->key == InputKeyRight) {
+ if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+ bt_mouse->wheel = MOUSE_SCROLL;
+ }
+ } else if(event->key == InputKeyLeft) {
+ if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+ bt_mouse->wheel = -MOUSE_SCROLL;
+ }
}
},
true);
diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/plugins/airmouse/views/usb_mouse.c
index 5d9ab4352..09075b566 100644
--- a/applications/plugins/airmouse/views/usb_mouse.c
+++ b/applications/plugins/airmouse/views/usb_mouse.c
@@ -21,6 +21,8 @@ static void usb_mouse_draw_callback(Canvas* canvas, void* context) {
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");
}
+#define MOUSE_SCROLL 2
+
static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) {
with_view_model(
usb_mouse->view,
@@ -45,6 +47,14 @@ static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) {
} else if(event->type == InputTypeRelease) {
furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL);
}
+ } else if(event->key == InputKeyRight) {
+ if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+ furi_hal_hid_mouse_scroll(MOUSE_SCROLL);
+ }
+ } else if(event->key == InputKeyLeft) {
+ if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+ furi_hal_hid_mouse_scroll(-MOUSE_SCROLL);
+ }
}
},
true);
diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam
index 3331888f2..5d5824e29 100644
--- a/applications/plugins/application.fam
+++ b/applications/plugins/application.fam
@@ -6,6 +6,5 @@ App(
"music_player",
"music_beeper",
"snake_game",
- "bt_hid",
],
)
diff --git a/applications/plugins/arkanoid/arkanoid_game.c b/applications/plugins/arkanoid/arkanoid_game.c
index af9976c98..f6d8c1a3d 100644
--- a/applications/plugins/arkanoid/arkanoid_game.c
+++ b/applications/plugins/arkanoid/arkanoid_game.c
@@ -37,6 +37,7 @@ typedef struct {
} BallState;
typedef struct {
+ FuriMutex* mutex;
BallState ball_state;
BrickState brick_state;
NotificationApp* notify;
@@ -309,10 +310,9 @@ static void arkanoid_state_init(ArkanoidState* arkanoid_state) {
}
static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) {
- ArkanoidState* arkanoid_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(arkanoid_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ ArkanoidState* arkanoid_state = ctx;
+ furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever);
//Initial level draw
if(!arkanoid_state->initialDraw) {
@@ -351,7 +351,7 @@ static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) {
arkanoid_state->score = 0;
}
- release_mutex((ValueMutex*)ctx, arkanoid_state);
+ furi_mutex_release(arkanoid_state->mutex);
}
static void arkanoid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -377,8 +377,8 @@ int32_t arkanoid_game_app(void* p) {
ArkanoidState* arkanoid_state = malloc(sizeof(ArkanoidState));
arkanoid_state_init(arkanoid_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, arkanoid_state, sizeof(ArkanoidState))) {
+ arkanoid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!arkanoid_state->mutex) {
FURI_LOG_E(TAG, "Cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
@@ -386,7 +386,7 @@ int32_t arkanoid_game_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, arkanoid_draw_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, arkanoid_draw_callback, arkanoid_state);
view_port_input_callback_set(view_port, arkanoid_input_callback, event_queue);
FuriTimer* timer =
@@ -400,7 +400,7 @@ int32_t arkanoid_game_app(void* p) {
GameEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- ArkanoidState* arkanoid_state = (ArkanoidState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// Key events
@@ -457,7 +457,7 @@ int32_t arkanoid_game_app(void* p) {
}
view_port_update(view_port);
- release_mutex(&state_mutex, arkanoid_state);
+ furi_mutex_release(arkanoid_state->mutex);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
@@ -465,7 +465,7 @@ int32_t arkanoid_game_app(void* p) {
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
- delete_mutex(&state_mutex);
+ furi_mutex_free(arkanoid_state->mutex);
free_and_exit:
free(arkanoid_state);
diff --git a/applications/plugins/asteroids/LICENSE b/applications/plugins/asteroids/LICENSE
new file mode 100644
index 000000000..2d8a8a74d
--- /dev/null
+++ b/applications/plugins/asteroids/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2022-2023 Salvatore Sanfilippo
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/applications/plugins/asteroids/app.c b/applications/plugins/asteroids/app.c
index 9aee8e9f0..495542a24 100644
--- a/applications/plugins/asteroids/app.c
+++ b/applications/plugins/asteroids/app.c
@@ -13,23 +13,51 @@
#include
#include
#include
+#include
#define TAG "Asteroids" // Used for logging
-#define DEBUG_MSG 1
+#define DEBUG_MSG 0
#define SCREEN_XRES 128
#define SCREEN_YRES 64
#define GAME_START_LIVES 3
+#define MAXLIVES 5 /* Max bonus lives allowed. */
#define TTLBUL 30 /* Bullet time to live, in ticks. */
-#define MAXBUL 5 /* Max bullets on the screen. */
+#define MAXBUL 50 /* Max bullets on the screen. */
+//@todo MAX Asteroids
#define MAXAST 32 /* Max asteroids on the screen. */
+#define MAXPOWERUPS 3 /* Max powerups allowed on screen */
+#define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */
#define SHIP_HIT_ANIMATION_LEN 15
-#define SAVING_DIRECTORY "/ext/apps_data/asteroids"
+#define SAVING_DIRECTORY "/ext/apps/Games"
#define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save"
#ifndef PI
#define PI 3.14159265358979f
#endif
/* ============================ Data structures ============================= */
+typedef enum PowerUpType {
+ PowerUpTypeShield, // Shield
+ PowerUpTypeLife, // Extra life
+ PowerUpTypeFirePower, // Burst Fire power
+ // PowerUpTypeRadialFire, // Radial Fire power
+ PowerUpTypeNuke, // Nuke power
+ // PowerUpTypeClone, // Clone ship
+ // PowerUpTypeAssist, // Secondary ship
+ Number_of_PowerUps // Used to count the number of powerups
+} PowerUpType;
+
+// struct PowerUp
+typedef struct PowerUp {
+ float x, y, vx, vy; /* Fields like in ship. */
+ // rot, /* Fields like ship. */
+ // rot_speed, /* Angular velocity (rot speed and sense). */
+ float size; /* Power Up size */
+
+ uint32_t ttl; /* Time to live, in ticks. */
+ uint32_t display_ttl; /* How long to display the powerup before it disappears */
+ enum PowerUpType powerUpType; /* PowerUp type */
+ bool isPowerUpActive; /* Is the powerup active? */
+} PowerUp;
typedef struct Ship {
float x, /* Ship x position. */
@@ -51,6 +79,7 @@ typedef struct Asteroid {
uint8_t shape_seed; /* Seed to give random shape. */
} Asteroid;
+// @todo AsteroidsApp
typedef struct AsteroidsApp {
/* GUI */
Gui* gui;
@@ -61,6 +90,7 @@ typedef struct AsteroidsApp {
/* Game state. */
int running; /* Once false exists the app. */
bool gameover; /* Gameover status. */
+ bool paused; /* Game paused status. */
uint32_t ticks; /* Game ticks. Increments at each refresh. */
uint32_t score; /* Game score. */
uint32_t highscore; /* Highscore. Shown on Game Over Screen */
@@ -73,11 +103,14 @@ typedef struct AsteroidsApp {
/* Ship state. */
struct Ship ship;
+ struct PowerUp powerUps[MAXPOWERUPS]; /* Each powerup state. */
+ int powerUps_num; /* Active powerups. */
/* Bullets state. */
struct Bullet bullets[MAXBUL]; /* Each bullet state. */
int bullets_num; /* Active bullets. */
uint32_t last_bullet_tick; /* Tick the last bullet was fired. */
+ uint32_t bullet_min_period; /* Minimum time between bullets in ms. */
/* Asteroids state. */
Asteroid asteroids[MAXAST]; /* Each asteroid state. */
@@ -133,9 +166,63 @@ const NotificationSequence sequence_bullet_fired = {
NULL,
};
+const NotificationSequence sequence_powerup_collected = {
+ &message_vibro_on,
+ &message_delay_1,
+ &message_delay_1,
+ &message_delay_1,
+ &message_delay_1,
+ &message_delay_1,
+ &message_vibro_off,
+ NULL,
+};
+
+const NotificationSequence sequence_nuke = {
+ &message_blink_set_color_red,
+ &message_blink_start_100,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_vibro_off,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_vibro_off,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_vibro_off,
+ &message_red_0,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_delay_1,
+ &message_delay_1,
+ &message_vibro_off,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_delay_1,
+ &message_delay_1,
+ &message_vibro_off,
+
+ &message_vibro_on,
+ &message_delay_10,
+ &message_delay_1,
+ &message_delay_1,
+ &message_vibro_off,
+
+ &message_blink_stop,
+ &message_vibro_off,
+ &message_sound_off,
+ NULL,
+};
+
/* ============================== Prototyeps ================================ */
// Only functions called before their definition are here.
+bool isPowerUpActive(AsteroidsApp* app, enum PowerUpType powerUpType);
+bool isPowerUpAlreadyExists(AsteroidsApp* app, enum PowerUpType powerUpType);
bool load_game(AsteroidsApp* app);
void save_game(AsteroidsApp* app);
void restart_game_after_gameover(AsteroidsApp* app);
@@ -183,6 +270,8 @@ void lfsr_next(unsigned char* prev) {
*prev ^= *prev << 7; /* Mix things a bit more. */
}
+/* ================================ Render ================================ */
+
/* Render the polygon 'poly' at x,y, rotated by the specified angle. */
void draw_poly(Canvas* const canvas, Poly* poly, uint8_t x, uint8_t y, float a) {
Poly rot;
@@ -249,20 +338,127 @@ void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) {
}
}
-/* Given the current position, update it according to the velocity and
- * wrap it back to the other side if the object went over the screen. */
-void update_pos_by_velocity(float* x, float* y, float vx, float vy) {
- /* Return back from one side to the other of the screen. */
- *x += vx;
- *y += vy;
- if(*x >= SCREEN_XRES)
- *x = 0;
- else if(*x < 0)
- *x = SCREEN_XRES - 1;
- if(*y >= SCREEN_YRES)
- *y = 0;
- else if(*y < 0)
- *y = SCREEN_YRES - 1;
+bool should_draw_powerUp(PowerUp* const p) {
+ // Just return if power up has already been picked up
+ if(p->display_ttl == 0) return false;
+
+ // Begin flashing power up when it is about to expire
+ if(p->display_ttl < 100) {
+ return p->display_ttl % 8 > 0;
+ }
+
+ return true;
+}
+
+void draw_powerUp_RemainingLife(Canvas* canvas, PowerUp* const p, int y_offset) {
+ if(!p->isPowerUpActive) return;
+
+ /*
+ Here we generate a reverse progress bar. The bar is 24 pixels wide and 1 pixel tall.
+ Calculate the percentage of hitpoints left: hitpoints / total hitpoints
+ Multiply the percentage by the width of the bar (in pixels): percentage * bar width
+ Subtract the result from the width of the bar to get the filled portion of the bar: bar width - (percentage * bar width)
+ Round the result to the nearest integer to get the final result.
+
+ 400 / 400 = 1.0
+ 1.0 * 24 = 24
+ 24 - 24 = 0
+ Round(0) = 0
+ */
+ int progress_bar_width = SCREEN_XRES / 4;
+
+ if(p->ttl > 0) {
+ canvas_set_color(canvas, ColorBlack);
+ int remaining = ceil(((float)p->ttl / (float)POWERUPSTTL) * (float)progress_bar_width);
+
+ if(remaining > 0) {
+ canvas_draw_line(
+ canvas,
+ (SCREEN_XRES / 2) - remaining, // x1
+ 3 + y_offset, //y1
+ (SCREEN_XRES / 2) + remaining, //x2
+ 3 + y_offset); // y2
+ }
+ }
+}
+
+void draw_powerUps(Canvas* const canvas, PowerUp* const p) {
+ /*
+
+ * * * * * * * * * *
+ * *
+ * *
+ * *
+ * F *
+ * *
+ * *
+ * *
+ * *
+ * * * * * * * * * *
+
+ BOX_SIZE = 10
+ Box_Width = BOX_SIZE
+ BOX_HEIGHT = BOX_SIZE
+ BOX_X_POS = x - BOX_WIDTH/2
+ BOX_Y_POS = y - BOX_HEIGHT/2
+ POS_F_X = WIDTH/2
+ POS_F_Y = HEIGHT/2
+
+ */
+
+ //@todo render_callback
+
+ // Just return if power up has already been picked up
+ // FURI_LOG_I(TAG, "[draw_powerUps] Display TTL: %lu", p->display_ttl);
+ if(p->display_ttl == 0) return;
+ if(!should_draw_powerUp(p)) return;
+
+ canvas_set_color(canvas, ColorXOR);
+
+ // Display power up to be picked up
+ switch(p->powerUpType) {
+ case PowerUpTypeFirePower:
+ canvas_draw_icon(canvas, p->x, p->y, &I_firepower_shifted_9x10);
+ break;
+ case PowerUpTypeShield:
+ canvas_draw_icon(canvas, p->x, p->y, &I_shield_frame);
+ break;
+ case PowerUpTypeLife:
+ // Draw a heart
+ canvas_draw_icon(canvas, p->x, p->y, &I_heart_10x10);
+ break;
+ case PowerUpTypeNuke:
+ // canvas_draw_disc(canvas, p->x, p->y, p->size);
+ // canvas_draw_str(canvas, p->x, p->y, "N");
+ canvas_draw_icon(canvas, p->x, p->y, &I_nuke_10x10);
+ break;
+ // case PowerUpTypeRadialFire:
+ // // Draw box with letter R inside
+ // canvas_draw_str(canvas, p->x, p->y, "R");
+ // break;
+ // case PowerUpTypeAssist:
+ // // Draw box with letter A inside
+ // canvas_draw_str(canvas, p->x, p->y, "A");
+ // break;
+ // case PowerUpTypeClone:
+ // // Draw box with letter C inside
+ // canvas_draw_str(canvas, p->x, p->y, "C");
+ // break;
+ default:
+ //@todo Uknown Power Up Type Detected
+ // Draw box with letter U inside
+ canvas_draw_str(canvas, p->x, p->y, "?");
+ FURI_LOG_E(TAG, "Unexpected Power Up Type Detected: %i", p->powerUpType);
+ break;
+ }
+}
+
+void draw_shield(Canvas* const canvas, AsteroidsApp* app) {
+ if(isPowerUpActive(app, PowerUpTypeShield) == false) return;
+
+ canvas_set_color(canvas, ColorXOR);
+ // canvas_draw_disc(canvas, app->ship.x, app->ship.y, 4);
+ canvas_draw_circle(canvas, app->ship.x, app->ship.y, 8);
}
/* Render the current game screen. */
@@ -286,6 +482,9 @@ void render_callback(Canvas* const canvas, void* ctx) {
/* Draw ship, asteroids, bullets. */
draw_poly(canvas, &ShipPoly, app->ship.x, app->ship.y, app->ship.rot);
+ /* Draw shield if active. */
+ draw_shield(canvas, app);
+
if(key_pressed_time(app, InputKeyUp) > 0) {
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_thrusters);
draw_poly(canvas, &ShipFirePoly, app->ship.x, app->ship.y, app->ship.rot);
@@ -295,6 +494,20 @@ void render_callback(Canvas* const canvas, void* ctx) {
for(int j = 0; j < app->asteroids_num; j++) draw_asteroid(canvas, &app->asteroids[j]);
+ for(int j = 0; j < app->powerUps_num; j++) {
+ draw_powerUps(canvas, &app->powerUps[j]);
+ draw_powerUp_RemainingLife(canvas, &app->powerUps[j], j);
+ }
+
+ if(app->paused) {
+ canvas_set_color(canvas, ColorXOR);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_rbox(canvas, 0, 0, SCREEN_XRES, SCREEN_YRES, 4);
+ canvas_draw_str_aligned(
+ canvas, SCREEN_XRES / 2, SCREEN_YRES / 2, AlignCenter, AlignCenter, "Paused");
+ return;
+ }
+
/* Game over text. */
if(app->gameover) {
canvas_set_color(canvas, ColorBlack);
@@ -331,6 +544,22 @@ void render_callback(Canvas* const canvas, void* ctx) {
/* ============================ Game logic ================================== */
+/* Given the current position, update it according to the velocity and
+ * wrap it back to the other side if the object went over the screen. */
+void update_pos_by_velocity(float* x, float* y, float vx, float vy) {
+ /* Return back from one side to the other of the screen. */
+ *x += vx;
+ *y += vy;
+ if(*x >= SCREEN_XRES)
+ *x = 0;
+ else if(*x < 0)
+ *x = SCREEN_XRES - 1;
+ if(*y >= SCREEN_YRES)
+ *y = 0;
+ else if(*y < 0)
+ *y = SCREEN_YRES - 1;
+}
+
float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
@@ -363,9 +592,16 @@ bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, flo
return dx * dx + dy * dy < rsum * rsum;
}
+/* ================================ Bullets ================================ */
+//@todo ship_fire_bullet
/* Create a new bullet headed in the same direction of the ship. */
void ship_fire_bullet(AsteroidsApp* app) {
- if(app->bullets_num == MAXBUL) return;
+ // No power ups, only 5 bullets allowed
+ if(isPowerUpActive(app, PowerUpTypeFirePower) == false && app->bullets_num >= 5) return;
+
+ // Double the Fire Power
+ if(isPowerUpActive(app, PowerUpTypeFirePower) && (app->bullets_num >= (MAXBUL))) return;
+
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired);
Bullet* b = &app->bullets[app->bullets_num];
b->x = app->ship.x;
@@ -401,6 +637,7 @@ void remove_bullet(AsteroidsApp* app, int bid) {
if(n && bid != n) app->bullets[bid] = app->bullets[n];
}
+/* ================================ Asteroids ================================ */
/* Create a new asteroid, away from the ship. Return the
* pointer to the asteroid object, so that the caller can change
* certain things of the asteroid if needed. */
@@ -466,6 +703,159 @@ void asteroid_was_hit(AsteroidsApp* app, int id) {
}
}
+/* ================================ Power Up ================================ */
+bool isPowerUpCollidingWithEachOther(AsteroidsApp* app, float x, float y, float size) {
+ for(int i = 0; i < app->powerUps_num; i++) {
+ PowerUp* p2 = &app->powerUps[i];
+ if(objects_are_colliding(x, y, size, p2->x, p2->y, p2->size, 1)) return true;
+ }
+ return false;
+}
+
+bool should_trigger_rare_powerUp(PowerUpType selected_powerUpType) {
+ switch(selected_powerUpType) {
+ case PowerUpTypeLife: // Make extra life power up more rare
+ return rand() % 10 != 0;
+ case PowerUpTypeShield: // Make shield power up more rare
+ return rand() % 4 != 0;
+ default:
+ return true;
+ }
+}
+
+//@todo Add PowerUp
+PowerUp* add_powerUp(AsteroidsApp* app) {
+ FURI_LOG_I(TAG, "add_powerUp: %i", app->powerUps_num);
+ if(app->powerUps_num == MAXPOWERUPS) return NULL; // Max Power Ups reached
+ if(app->lives == MAXLIVES) return NULL; // Max Lives reached
+
+ // Randomly select power up for display
+ PowerUpType selected_powerUpType = rand() % Number_of_PowerUps;
+ FURI_LOG_I(TAG, "[add_powerUp] Power Up Selected: %i", selected_powerUpType);
+
+ // Don't add already existing power ups
+ if(isPowerUpAlreadyExists(app, selected_powerUpType)) {
+ FURI_LOG_D(TAG, "[add_powerUp] Power Up %i already active", selected_powerUpType);
+ return NULL;
+ }
+
+ // Make some power ups more rare
+ if(!should_trigger_rare_powerUp(selected_powerUpType)) {
+ FURI_LOG_D(TAG, "[add_powerUp] Power Up %i not triggered", selected_powerUpType);
+ return NULL;
+ }
+
+ float size = 10;
+ float min_distance = 20;
+ float x, y;
+ do {
+ //Make sure power up is not spawned on the edge of the screen
+ x = rand() % (SCREEN_XRES - (int)size);
+ y = rand() % (SCREEN_YRES - (int)size);
+
+ //Also keep it away from the lives and score at the top of screen
+ if(y < size) y = size;
+ } while(
+ ((distance(app->ship.x, app->ship.y, x, y) < min_distance + size) ||
+ isPowerUpCollidingWithEachOther(app, x, y, size)));
+
+ PowerUp* p = &app->powerUps[app->powerUps_num++];
+ p->x = x;
+ p->y = y;
+ //@todo Disable Velocity
+ p->vx = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
+ p->vy = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
+ p->display_ttl = 200;
+ p->ttl = POWERUPSTTL;
+ p->size = size;
+ // p->size = size;
+ // p->rot = 0;
+ // p->rot_speed = ((float)rand() / RAND_MAX) / 10;
+ // if(app->ticks & 1) p->rot_speed = -(p->rot_speed);
+
+ //@todo add powerup type, for now hardcoding to firepower
+ p->powerUpType = selected_powerUpType;
+ p->isPowerUpActive = false;
+ FURI_LOG_I(TAG, "[add_powerUp] Power Up Added: %i", p->powerUpType);
+ return p;
+}
+
+bool isPowerUpActive(AsteroidsApp* const app, PowerUpType const powerUpType) {
+ for(int i = 0; i < app->powerUps_num; i++) {
+ // PowerUp* p = &app->powerUps[i];
+ // if(p->powerUpType == powerUpType && p->ttl > 0 && p->display_ttl == 0) return true;
+ if(app->powerUps[i].isPowerUpActive && app->powerUps[i].powerUpType == powerUpType) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isPowerUpAlreadyExists(AsteroidsApp* const app, PowerUpType const powerUpType) {
+ for(int i = 0; i < app->powerUps_num; i++) {
+ if(app->powerUps[i].powerUpType == powerUpType) return true;
+ }
+ return false;
+}
+
+//@todo remove_powerUp
+void remove_powerUp(AsteroidsApp* app, int id) {
+ FURI_LOG_I(TAG, "remove_powerUp: %i", id);
+ // Invalid ID, ignore
+ if(id < 0) {
+ FURI_LOG_E(TAG, "remove_powerUp: Invalid ID: %i", id);
+ return;
+ }
+ // TODO: Break this out into object types that set the game state
+ // Return the bullet period to normal
+ if(app->powerUps[id].powerUpType == PowerUpTypeFirePower) {
+ app->bullet_min_period = 200;
+ }
+
+ /* Replace the top powerUp with the empty space left
+ * by the removal of this one. This way we always take the
+ * array dense, which is an advantage when looping. */
+ int n = --app->powerUps_num;
+ if(n && id != n) app->powerUps[id] = app->powerUps[n];
+}
+
+void remove_all_astroids_and_bullets(AsteroidsApp* app) {
+ app->score += app->asteroids_num;
+ app->asteroids_num = 0;
+ app->bullets_num = 0;
+}
+
+//@todo powerUp_was_hit
+void powerUp_was_hit(AsteroidsApp* app, int id) {
+ PowerUp* p = &app->powerUps[id];
+ if(p->display_ttl == 0) return; // Don't collect if already collected or expired
+
+ // Vibrate to indicate power up was collected
+ notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_powerup_collected);
+
+ switch(p->powerUpType) {
+ case PowerUpTypeLife:
+ if(app->lives < MAXLIVES) app->lives++;
+ remove_powerUp(app, id);
+ break;
+ case PowerUpTypeFirePower:
+ p->ttl = POWERUPSTTL / 2;
+ app->bullet_min_period = 100;
+ break;
+ case PowerUpTypeNuke:
+ //TODO: Animate explosion
+ notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_nuke);
+ // Simulate nuke explosion
+ remove_all_astroids_and_bullets(app);
+ break;
+ default:
+ break;
+ }
+ p->display_ttl = 0;
+ p->isPowerUpActive = true;
+}
+
+/* ================================ Game States ================================ */
/* Set gameover state. When in game-over mode, the game displays a gameover
* text with a background of many asteroids floating around. */
void game_over(AsteroidsApp* app) {
@@ -494,7 +884,9 @@ void restart_game(AsteroidsApp* app) {
app->ship.vx = 0;
app->ship.vy = 0;
app->bullets_num = 0;
+ app->powerUps_num = 0;
app->last_bullet_tick = 0;
+ app->bullet_min_period = 200;
app->asteroids_num = 0;
app->ship_hit = 0;
}
@@ -510,6 +902,7 @@ void restart_game_after_gameover(AsteroidsApp* app) {
restart_game(app);
}
+/* ================================ Position & Status ================================ */
/* Move bullets. */
void update_bullets_position(AsteroidsApp* app) {
for(int j = 0; j < app->bullets_num; j++) {
@@ -536,6 +929,68 @@ void update_asteroids_position(AsteroidsApp* app) {
}
}
+bool should_add_powerUp(AsteroidsApp* app) {
+ srand(furi_get_tick());
+ int random_number = rand() % 100 + 1;
+
+ // The chance of spawning a power-up decreases as the game goes on
+ int threshold = 100 - (app->score * 5);
+
+ // Make sure the threshold doesn't go below 10
+ threshold = (threshold < 10) ? 10 : threshold;
+ // FURI_LOG_I(
+ // TAG,
+ // "Random number: %d, threshold: %d Bool: %d",
+ // random_number,
+ // threshold,
+ // random_number <= threshold);
+ return random_number <= threshold;
+}
+
+void update_powerUps_position(AsteroidsApp* app) {
+ for(int j = 0; j < app->powerUps_num; j++) {
+ // @todo update_powerUps_position
+ if(app->powerUps[j].display_ttl > 0) {
+ update_pos_by_velocity(
+ &app->powerUps[j].x, &app->powerUps[j].y, app->powerUps[j].vx, app->powerUps[j].vy);
+ }
+ }
+}
+
+// @todo update_powerUp_status
+/* This updates the state of each power up collected and removes them if they have expired. */
+void update_powerUp_status(AsteroidsApp* app) {
+ for(int j = 0; j < app->powerUps_num; j++) {
+ if(app->powerUps[j].ttl > 0 && app->powerUps[j].isPowerUpActive) {
+ // Only decrement ttl if we actually picked up power up
+ app->powerUps[j].ttl--;
+ } else if(app->powerUps[j].display_ttl > 0) {
+ app->powerUps[j].display_ttl--;
+ } else if(app->powerUps[j].ttl == 0 || app->powerUps[j].display_ttl == 0) {
+ FURI_LOG_I(
+ TAG,
+ "[update_powerUp_status] Power up expired!, ttl: %lu, display_ttl: %lu id: %d",
+ app->powerUps[j].ttl,
+ app->powerUps[j].display_ttl,
+ j);
+ // we've reached the end of life of the power up
+ // Time to remove it
+ app->powerUps[j].isPowerUpActive = false;
+ remove_powerUp(app, j);
+ j--; /* Process this power up index again: the removal will
+ fill it with the top power up to take the array dense. */
+ } else {
+ FURI_LOG_E(
+ TAG,
+ "[update_powerUp_status] Power up error! Invalid Index: %d ttl: %lu display_ttl: %lu PowerUp_Num: %d",
+ j,
+ app->powerUps[j].ttl,
+ app->powerUps[j].display_ttl,
+ app->powerUps_num);
+ }
+ }
+}
+
/* Collision detection and game state update based on collisions. */
void detect_collisions(AsteroidsApp* app) {
/* Detect collision between bullet and asteroid. */
@@ -560,8 +1015,26 @@ void detect_collisions(AsteroidsApp* app) {
for(int j = 0; j < app->asteroids_num; j++) {
Asteroid* a = &app->asteroids[j];
if(objects_are_colliding(a->x, a->y, a->size, app->ship.x, app->ship.y, 4, 1)) {
- ship_was_hit(app);
- break;
+ if(isPowerUpActive(app, PowerUpTypeShield)) {
+ // Asteroid was hit with shield
+ notification_message(
+ furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired);
+ asteroid_was_hit(app, j);
+ j--; /* Scan this j value again. */
+ } else {
+ // No sheild active, take damage
+ ship_was_hit(app);
+ break;
+ }
+ }
+ }
+
+ /* Detect collision between ship and powerUp. */
+ for(int j = 0; j < app->powerUps_num; j++) {
+ PowerUp* p = &app->powerUps[j];
+ if(objects_are_colliding(p->x, p->y, p->size, app->ship.x, app->ship.y, 4, 1)) {
+ powerUp_was_hit(app, j);
+ // break;
}
}
}
@@ -599,6 +1072,12 @@ void game_tick(void* ctx) {
update_asteroids_position(app);
view_port_update(app->view_port);
return;
+ } else if(app->paused) {
+ if(key_pressed_time(app, InputKeyBack) > 100 || key_pressed_time(app, InputKeyOk) > 100) {
+ app->paused = false;
+ }
+ view_port_update(app->view_port);
+ return;
}
/* Handle keypresses. */
@@ -617,19 +1096,32 @@ void game_tick(void* ctx) {
* asteroids_update_keypress_state() since depends on exact
* pressure timing. */
if(app->fire) {
- uint32_t bullet_min_period = 200; // In milliseconds
uint32_t now = furi_get_tick();
- if(now - app->last_bullet_tick >= bullet_min_period) {
+ if(now - app->last_bullet_tick >= app->bullet_min_period) {
ship_fire_bullet(app);
app->last_bullet_tick = now;
}
app->fire = false;
}
+ // DEBUG: Show Power Up Status
+ // for(int j = 0; j < app->powerUps_num; j++) {
+ // PowerUp* p = &app->powerUps[j];
+ // FURI_LOG_I(
+ // TAG,
+ // "Power Up Type: %d TTL: %lu Display_TTL: %lu PowerUpNum: %i",
+ // p->powerUpType,
+ // p->ttl,
+ // p->display_ttl,
+ // app->powerUps_num);
+ // }
+
/* Update positions and detect collisions. */
update_pos_by_velocity(&app->ship.x, &app->ship.y, app->ship.vx, app->ship.vy);
update_bullets_position(app);
update_asteroids_position(app);
+ update_powerUp_status(app); //@todo update_powerUp_status
+ update_powerUps_position(app);
detect_collisions(app);
/* From time to time, create a new asteroid. The more asteroids
@@ -639,6 +1131,13 @@ void game_tick(void* ctx) {
add_asteroid(app);
}
+ /* From time to time add a random power up */
+ //@todo game tick
+ // if(app->powerUps_num == 0 || random() % (500 + (100 * (int)app->score)) <= app->powerUps_num) {
+ if(should_add_powerUp(app)) {
+ add_powerUp(app);
+ }
+
app->ticks++;
view_port_update(app->view_port);
}
@@ -760,8 +1259,17 @@ int32_t asteroids_app_entry(void* p) {
while(app->running) {
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
if(qstat == FuriStatusOk) {
- if(DEBUG_MSG)
- FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
+ // if(DEBUG_MSG)
+ // FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
+ /* Handle Pause */
+ if(input.type == InputTypeShort && input.key == InputKeyBack) {
+ app->paused = !app->paused;
+ if(app->paused) {
+ furi_timer_stop(timer);
+ } else {
+ furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10);
+ }
+ }
/* Handle navigation here. Then handle view-specific inputs
* in the view specific handling function. */
@@ -775,11 +1283,11 @@ int32_t asteroids_app_entry(void* p) {
} else {
/* Useful to understand if the app is still alive when it
* does not respond because of bugs. */
- if(DEBUG_MSG) {
- static int c = 0;
- c++;
- if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
- }
+ // if(DEBUG_MSG) {
+ // static int c = 0;
+ // c++;
+ // if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
+ // }
}
}
diff --git a/applications/plugins/asteroids/application.fam b/applications/plugins/asteroids/application.fam
index f5ad2cdb9..5eb43a6e5 100644
--- a/applications/plugins/asteroids/application.fam
+++ b/applications/plugins/asteroids/application.fam
@@ -3,10 +3,14 @@ App(
name="Asteroids",
apptype=FlipperAppType.EXTERNAL,
entry_point="asteroids_app_entry",
- cdefines=["APP_PROTOVIEW"],
+ cdefines=["APP_ASTEROIDS"],
requires=["gui"],
stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
fap_category="Games",
+ fap_icon_assets="assets", # Image assets to compile for this application
+ fap_description="An implementation of the classic arcade game Asteroids",
+ fap_author="antirez, SimplyMinimal",
+ fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids",
)
diff --git a/applications/plugins/asteroids/assets/ammo_10x10.png b/applications/plugins/asteroids/assets/ammo_10x10.png
new file mode 100644
index 000000000..b112a1a7f
Binary files /dev/null and b/applications/plugins/asteroids/assets/ammo_10x10.png differ
diff --git a/applications/plugins/asteroids/assets/ammo_11x11.png b/applications/plugins/asteroids/assets/ammo_11x11.png
new file mode 100644
index 000000000..55e59f858
Binary files /dev/null and b/applications/plugins/asteroids/assets/ammo_11x11.png differ
diff --git a/applications/plugins/asteroids/assets/firepower_12x12.png b/applications/plugins/asteroids/assets/firepower_12x12.png
new file mode 100644
index 000000000..711a29200
Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_12x12.png differ
diff --git a/applications/plugins/asteroids/assets/firepower_9x10.png b/applications/plugins/asteroids/assets/firepower_9x10.png
new file mode 100644
index 000000000..4070b6c88
Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_9x10.png differ
diff --git a/applications/plugins/asteroids/assets/firepower_shifted_9x10.png b/applications/plugins/asteroids/assets/firepower_shifted_9x10.png
new file mode 100644
index 000000000..9c8506d18
Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_shifted_9x10.png differ
diff --git a/applications/plugins/asteroids/assets/heart_10x10.png b/applications/plugins/asteroids/assets/heart_10x10.png
new file mode 100644
index 000000000..0d66b49ee
Binary files /dev/null and b/applications/plugins/asteroids/assets/heart_10x10.png differ
diff --git a/applications/plugins/asteroids/assets/heart_12x12.png b/applications/plugins/asteroids/assets/heart_12x12.png
new file mode 100644
index 000000000..b1cfdcdfe
Binary files /dev/null and b/applications/plugins/asteroids/assets/heart_12x12.png differ
diff --git a/applications/plugins/asteroids/assets/nuke_10x10.png b/applications/plugins/asteroids/assets/nuke_10x10.png
new file mode 100644
index 000000000..8b49fc98e
Binary files /dev/null and b/applications/plugins/asteroids/assets/nuke_10x10.png differ
diff --git a/applications/plugins/asteroids/assets/shield-frame.png b/applications/plugins/asteroids/assets/shield-frame.png
new file mode 100644
index 000000000..60a670f0e
Binary files /dev/null and b/applications/plugins/asteroids/assets/shield-frame.png differ
diff --git a/applications/plugins/asteroids/assets/shield_clean.png b/applications/plugins/asteroids/assets/shield_clean.png
new file mode 100644
index 000000000..59eefb717
Binary files /dev/null and b/applications/plugins/asteroids/assets/shield_clean.png differ
diff --git a/applications/plugins/asteroids/assets/split_shield_10x10.png b/applications/plugins/asteroids/assets/split_shield_10x10.png
new file mode 100644
index 000000000..bff879ca1
Binary files /dev/null and b/applications/plugins/asteroids/assets/split_shield_10x10.png differ
diff --git a/applications/plugins/barcode_generator/application.fam b/applications/plugins/barcode_generator/application.fam
index 97dc9acef..a02255334 100644
--- a/applications/plugins/barcode_generator/application.fam
+++ b/applications/plugins/barcode_generator/application.fam
@@ -9,7 +9,7 @@ App(
"dialogs",
],
stack_size=1 * 1024,
- order=250,
+ order=50,
fap_icon="barcode_10px.png",
fap_category="Misc",
)
diff --git a/applications/plugins/barcode_generator/barcode_generator.c b/applications/plugins/barcode_generator/barcode_generator.c
index 4aa54e7d4..2645bbcea 100644
--- a/applications/plugins/barcode_generator/barcode_generator.c
+++ b/applications/plugins/barcode_generator/barcode_generator.c
@@ -1,8 +1,3 @@
-#include
-#include
-#include
-#include
-
#include "barcode_generator.h"
static BarcodeType* barcodeTypes[NUMBER_OF_BARCODE_TYPES];
@@ -103,9 +98,9 @@ int get_menu_text_location(int index) {
}
int get_barcode_max_index(PluginState* plugin_state) {
- return plugin_state->doParityCalculation ?
- barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 :
- barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits;
+ return plugin_state->barcode_state.doParityCalculation ?
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits - 1 :
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits;
}
int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
@@ -114,12 +109,12 @@ int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
int checkDigitEven = 0;
//add all odd positions. Confusing because 0index
for(int i = 0; i < type->numberOfDigits - 1; i += 2) {
- checkDigitOdd += plugin_state->barcodeNumeral[i];
+ checkDigitOdd += plugin_state->barcode_state.barcodeNumeral[i];
}
//add all even positions to above. Confusing because 0index
for(int i = 1; i < type->numberOfDigits - 1; i += 2) {
- checkDigitEven += plugin_state->barcodeNumeral[i];
+ checkDigitEven += plugin_state->barcode_state.barcodeNumeral[i];
}
if(type->bartype == BarTypeEAN13) {
@@ -135,10 +130,9 @@ int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
}
static void render_callback(Canvas* const canvas, void* ctx) {
- PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(plugin_state->mode == MenuMode) {
canvas_set_color(canvas, ColorBlack);
@@ -152,7 +146,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas, 64, get_menu_text_location(2), AlignCenter, AlignCenter, "Parity?");
canvas_draw_frame(canvas, 83, get_menu_text_location(2) - 3, 6, 6);
- if(plugin_state->doParityCalculation == true) {
+ if(plugin_state->barcode_state.doParityCalculation == true) {
canvas_draw_box(canvas, 85, get_menu_text_location(2) - 1, 2, 2);
}
canvas_draw_str_aligned(
@@ -161,14 +155,14 @@ static void render_callback(Canvas* const canvas, void* ctx) {
get_menu_text_location(3),
AlignCenter,
AlignCenter,
- (barcodeTypes[plugin_state->barcodeTypeIndex])->name);
+ (barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex])->name);
canvas_draw_disc(
canvas,
40,
get_menu_text_location(plugin_state->menuIndex) - 1,
2); //draw menu cursor
} else {
- BarcodeType* type = barcodeTypes[plugin_state->barcodeTypeIndex];
+ BarcodeType* type = barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex];
//start saftey
canvas_set_color(canvas, ColorBlack);
@@ -181,13 +175,13 @@ static void render_callback(Canvas* const canvas, void* ctx) {
startpos++;
draw_digit(
canvas,
- plugin_state->barcodeNumeral[0],
+ plugin_state->barcode_state.barcodeNumeral[0],
BarEncodingTypeRight,
- get_digit_position(0, barcodeTypes[plugin_state->barcodeTypeIndex]),
+ get_digit_position(0, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]),
false);
}
- if(plugin_state->doParityCalculation) { //calculate the check digit
- plugin_state->barcodeNumeral[type->numberOfDigits - 1] =
+ if(plugin_state->barcode_state.doParityCalculation) { //calculate the check digit
+ plugin_state->barcode_state.barcodeNumeral[type->numberOfDigits - 1] =
calculate_check_digit(plugin_state, type);
}
for(int index = startpos; index < endpos; index++) {
@@ -197,7 +191,9 @@ static void render_callback(Canvas* const canvas, void* ctx) {
barEncodingType = BarEncodingTypeRight;
} else {
barEncodingType =
- (FURI_BIT(EAN13ENCODE[plugin_state->barcodeNumeral[0]], index - 1)) ?
+ (FURI_BIT(
+ EAN13ENCODE[plugin_state->barcode_state.barcodeNumeral[0]],
+ index - 1)) ?
BarEncodingTypeG :
BarEncodingTypeLeft;
}
@@ -207,10 +203,14 @@ static void render_callback(Canvas* const canvas, void* ctx) {
}
}
- int digitPosition =
- get_digit_position(index, barcodeTypes[plugin_state->barcodeTypeIndex]);
+ int digitPosition = get_digit_position(
+ index, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]);
draw_digit(
- canvas, plugin_state->barcodeNumeral[index], barEncodingType, digitPosition, true);
+ canvas,
+ plugin_state->barcode_state.barcodeNumeral[index],
+ barEncodingType,
+ digitPosition,
+ true);
}
//central separator
@@ -223,7 +223,8 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_box(
canvas,
get_digit_position(
- plugin_state->editingIndex, barcodeTypes[plugin_state->barcodeTypeIndex]) -
+ plugin_state->editingIndex,
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]) -
1,
63,
7,
@@ -237,7 +238,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_box(canvas, (endSafetyPosition + 2), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -247,15 +248,17 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
-static void barcode_generator_state_init(PluginState* const plugin_state) {
- for(int i = 0; i < BARCODE_MAX_LENS; ++i) {
- plugin_state->barcodeNumeral[i] = i % 10;
- }
+static void barcode_generator_state_init(PluginState* plugin_state) {
plugin_state->editingIndex = 0;
plugin_state->mode = ViewMode;
- plugin_state->doParityCalculation = true;
plugin_state->menuIndex = MENU_INDEX_VIEW;
- plugin_state->barcodeTypeIndex = 0;
+ if(!LOAD_BARCODE_SETTINGS(&plugin_state->barcode_state)) {
+ for(int i = 0; i < BARCODE_MAX_LENS; ++i) {
+ plugin_state->barcode_state.barcodeNumeral[i] = i % 10;
+ }
+ plugin_state->barcode_state.doParityCalculation = true;
+ plugin_state->barcode_state.barcodeTypeIndex = 0;
+ }
}
static bool handle_key_press_view(InputKey key, PluginState* plugin_state) {
@@ -277,15 +280,15 @@ static bool handle_key_press_edit(InputKey key, PluginState* plugin_state) {
switch(key) {
case InputKeyUp:
- plugin_state->barcodeNumeral[plugin_state->editingIndex] =
- (plugin_state->barcodeNumeral[plugin_state->editingIndex] + 1) % 10;
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] =
+ (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] + 1) % 10;
break;
case InputKeyDown:
- plugin_state->barcodeNumeral[plugin_state->editingIndex] =
- (plugin_state->barcodeNumeral[plugin_state->editingIndex] == 0) ?
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] =
+ (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] == 0) ?
9 :
- plugin_state->barcodeNumeral[plugin_state->editingIndex] - 1;
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] - 1;
break;
case InputKeyRight:
@@ -324,21 +327,24 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) {
case InputKeyRight:
if(plugin_state->menuIndex == MENU_INDEX_TYPE) {
- plugin_state->barcodeTypeIndex =
- (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
0 :
- plugin_state->barcodeTypeIndex + 1;
+ plugin_state->barcode_state.barcodeTypeIndex + 1;
} else if(plugin_state->menuIndex == MENU_INDEX_PARITY) {
- plugin_state->doParityCalculation = !plugin_state->doParityCalculation;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_state.doParityCalculation;
}
break;
case InputKeyLeft:
if(plugin_state->menuIndex == MENU_INDEX_TYPE) {
- plugin_state->barcodeTypeIndex = (plugin_state->barcodeTypeIndex == 0) ?
- NUMBER_OF_BARCODE_TYPES - 1 :
- plugin_state->barcodeTypeIndex - 1;
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == 0) ?
+ NUMBER_OF_BARCODE_TYPES - 1 :
+ plugin_state->barcode_state.barcodeTypeIndex - 1;
} else if(plugin_state->menuIndex == MENU_INDEX_PARITY) {
- plugin_state->doParityCalculation = !plugin_state->doParityCalculation;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_state.doParityCalculation;
}
break;
@@ -348,12 +354,13 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) {
} else if(plugin_state->menuIndex == MENU_INDEX_EDIT) {
plugin_state->mode = EditMode;
} else if(plugin_state->menuIndex == MENU_INDEX_PARITY) {
- plugin_state->doParityCalculation = !plugin_state->doParityCalculation;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_state.doParityCalculation;
} else if(plugin_state->menuIndex == MENU_INDEX_TYPE) {
- plugin_state->barcodeTypeIndex =
- (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
0 :
- plugin_state->barcodeTypeIndex + 1;
+ plugin_state->barcode_state.barcodeTypeIndex + 1;
}
break;
@@ -379,8 +386,9 @@ int32_t barcode_generator_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
barcode_generator_state_init(plugin_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("barcode_generator", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
@@ -389,7 +397,7 @@ int32_t barcode_generator_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -399,7 +407,7 @@ int32_t barcode_generator_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -422,7 +430,7 @@ int32_t barcode_generator_app(void* p) {
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
view_port_enabled_set(view_port, false);
@@ -430,6 +438,10 @@ int32_t barcode_generator_app(void* p) {
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
+ furi_mutex_free(plugin_state->mutex);
+ // save settings
+ SAVE_BARCODE_SETTINGS(&plugin_state->barcode_state);
+ free(plugin_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/barcode_generator/barcode_generator.h b/applications/plugins/barcode_generator/barcode_generator.h
index 1b5ff9e15..5d2c8307e 100644
--- a/applications/plugins/barcode_generator/barcode_generator.h
+++ b/applications/plugins/barcode_generator/barcode_generator.h
@@ -1,3 +1,34 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define BARCODE_SETTINGS_FILE_NAME "apps/Misc/barcodegen.save"
+
+#define BARCODE_SETTINGS_VER (1)
+#define BARCODE_SETTINGS_PATH EXT_PATH(BARCODE_SETTINGS_FILE_NAME)
+#define BARCODE_SETTINGS_MAGIC (0xC2)
+
+#define SAVE_BARCODE_SETTINGS(x) \
+ saved_struct_save( \
+ BARCODE_SETTINGS_PATH, \
+ (x), \
+ sizeof(BarcodeState), \
+ BARCODE_SETTINGS_MAGIC, \
+ BARCODE_SETTINGS_VER)
+
+#define LOAD_BARCODE_SETTINGS(x) \
+ saved_struct_load( \
+ BARCODE_SETTINGS_PATH, \
+ (x), \
+ sizeof(BarcodeState), \
+ BARCODE_SETTINGS_MAGIC, \
+ BARCODE_SETTINGS_VER)
+
#define BARCODE_HEIGHT 50
#define BARCODE_Y_START 3
#define BARCODE_TEXT_OFFSET 9
@@ -45,11 +76,16 @@ typedef struct {
typedef struct {
int barcodeNumeral[BARCODE_MAX_LENS]; //The current barcode number
+ bool doParityCalculation; //Should do parity check?
+ int barcodeTypeIndex;
+} BarcodeState;
+
+typedef struct {
+ FuriMutex* mutex;
+ BarcodeState barcode_state;
int editingIndex; //The index of the editing symbol
int menuIndex; //The index of the menu cursor
Mode mode; //View, edit or menu
- bool doParityCalculation; //Should do parity check?
- int barcodeTypeIndex;
} PluginState;
static const int DIGITS[10][4] = {
diff --git a/applications/plugins/blackjack/application.fam b/applications/plugins/blackjack/application.fam
index 489ce2aeb..6ca8add37 100644
--- a/applications/plugins/blackjack/application.fam
+++ b/applications/plugins/blackjack/application.fam
@@ -1,5 +1,5 @@
App(
- appid="BlackJack",
+ appid="Blackjack",
name="BlackJack",
apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app",
diff --git a/applications/plugins/blackjack/blackjack.c b/applications/plugins/blackjack/blackjack.c
index 73d393f8b..8b81829c3 100644
--- a/applications/plugins/blackjack/blackjack.c
+++ b/applications/plugins/blackjack/blackjack.c
@@ -1,7 +1,6 @@
#include
#include
-#include
#include
#include
@@ -14,7 +13,7 @@
#include "util.h"
#include "ui.h"
-#include "BlackJack_icons.h"
+#include "Blackjack_icons.h"
#define DEALER_MAX 17
@@ -33,11 +32,9 @@ static void draw_ui(Canvas* const canvas, const GameState* game_state) {
}
static void render_callback(Canvas* const canvas, void* ctx) {
- const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25);
-
- if(game_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const GameState* game_state = ctx;
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -60,7 +57,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
settings_page(canvas, game_state);
}
- release_mutex((ValueMutex*)ctx, game_state);
+ furi_mutex_release(game_state->mutex);
}
//region card draw
@@ -552,68 +549,65 @@ int32_t blackjack_app(void* p) {
game_state->state = GameStateStart;
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
+ game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!game_state->mutex) {
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
}
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, game_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25);
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyUp:
- localstate->selectDirection = DirectionUp;
+ game_state->selectDirection = DirectionUp;
break;
case InputKeyDown:
- localstate->selectDirection = DirectionDown;
+ game_state->selectDirection = DirectionDown;
break;
case InputKeyRight:
- localstate->selectDirection = DirectionRight;
+ game_state->selectDirection = DirectionRight;
break;
case InputKeyLeft:
- localstate->selectDirection = DirectionLeft;
+ game_state->selectDirection = DirectionLeft;
break;
case InputKeyBack:
- if(localstate->state == GameStateSettings) {
- localstate->state = GameStateStart;
- save_settings(localstate->settings);
+ if(game_state->state == GameStateSettings) {
+ game_state->state = GameStateStart;
+ save_settings(game_state->settings);
} else
processing = false;
break;
case InputKeyOk:
- localstate->selectDirection = Select;
+ game_state->selectDirection = Select;
break;
default:
break;
}
}
} else if(event.type == EventTypeTick) {
- tick(localstate);
- processing = localstate->processing;
+ tick(game_state);
+ processing = game_state->processing;
}
- } else {
- //FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout");
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, localstate);
+ furi_mutex_release(game_state->mutex);
}
furi_timer_free(timer);
@@ -621,7 +615,7 @@ int32_t blackjack_app(void* p) {
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
- delete_mutex(&state_mutex);
+ furi_mutex_free(game_state->mutex);
free_and_exit:
free(game_state->deck.cards);
@@ -631,4 +625,4 @@ free_and_exit:
furi_message_queue_free(event_queue);
return return_code;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/blackjack/common/card.c b/applications/plugins/blackjack/common/card.c
index 199135bb5..88228fda4 100644
--- a/applications/plugins/blackjack/common/card.c
+++ b/applications/plugins/blackjack/common/card.c
@@ -173,7 +173,7 @@ uint8_t hand_count(const Card* cards, uint8_t count) {
}
for(uint8_t i = 0; i < aceCount; i++) {
- if((score + 11) <= 21)
+ if((score + 11 + (aceCount - 1)) <= 21)
score += 11;
else
score++;
@@ -350,4 +350,4 @@ void add_hand_region(Hand* to, Hand* from) {
add_to_hand(to, from->cards[i]);
}
}
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/blackjack/defines.h b/applications/plugins/blackjack/defines.h
index b400badfb..0a3fdf53e 100644
--- a/applications/plugins/blackjack/defines.h
+++ b/applications/plugins/blackjack/defines.h
@@ -54,6 +54,7 @@ typedef enum {
} Direction;
typedef struct {
+ FuriMutex* mutex;
Card player_cards[21];
Card dealer_cards[21];
uint8_t player_card_count;
diff --git a/applications/plugins/bpmtapper/README.md b/applications/plugins/bpmtapper/README.md
index 224d2cb5b..8e88863ee 100644
--- a/applications/plugins/bpmtapper/README.md
+++ b/applications/plugins/bpmtapper/README.md
@@ -9,6 +9,6 @@ Hit any button other than back repeatedly. Calculates based on the average of th
## Compiling
```
-./fbt fap_bpm_tapper
+./fbt firmware_bpm_tapper
```
diff --git a/applications/plugins/bpmtapper/bpm.c b/applications/plugins/bpmtapper/bpm.c
index 323a898a4..5720ac4d2 100644
--- a/applications/plugins/bpmtapper/bpm.c
+++ b/applications/plugins/bpmtapper/bpm.c
@@ -3,7 +3,6 @@
#include
#include
#include
-#include
#include
#include "BPM_Tapper_icons.h"
@@ -91,6 +90,7 @@ static float queue_avg(queue* q) {
//}
//
typedef struct {
+ FuriMutex* mutex;
int taps;
double bpm;
uint32_t last_stamp;
@@ -127,42 +127,43 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
}
static void render_callback(Canvas* const canvas, void* ctx) {
- string_t tempStr;
+ furi_assert(ctx);
+ const BPMTapper* bpm_state = ctx;
+ furi_mutex_acquire(bpm_state->mutex, FuriWaitForever);
- const BPMTapper* bpm_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(bpm_state == NULL) {
- return;
- }
+ FuriString* tempStr;
// border
//canvas_draw_frame(canvas, 0, 0, 128, 64);
canvas_set_font(canvas, FontPrimary);
- string_init(tempStr);
+ tempStr = furi_string_alloc();
- string_printf(tempStr, "Taps: %d", bpm_state->taps);
- canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr));
- string_reset(tempStr);
+ furi_string_printf(tempStr, "Taps: %d", bpm_state->taps);
+ canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
- string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size);
- canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr));
- string_reset(tempStr);
+ furi_string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size);
+ canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
- string_printf(tempStr, "Interval: %dms", bpm_state->interval);
- canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, string_get_cstr(tempStr));
- string_reset(tempStr);
+ furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval);
+ canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
- string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2);
- canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignCenter, string_get_cstr(tempStr));
- string_reset(tempStr);
+ furi_string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2);
+ canvas_draw_str_aligned(
+ canvas, 64, 60, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
- string_printf(tempStr, "%.2f", bpm_state->bpm);
+ furi_string_printf(tempStr, "%.2f", bpm_state->bpm);
canvas_set_font(canvas, FontBigNumbers);
- canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, string_get_cstr(tempStr));
- string_reset(tempStr);
+ canvas_draw_str_aligned(
+ canvas, 64, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
- string_clear(tempStr);
+ furi_string_free(tempStr);
- release_mutex((ValueMutex*)ctx, bpm_state);
+ furi_mutex_release(bpm_state->mutex);
}
static void bpm_state_init(BPMTapper* const plugin_state) {
@@ -185,8 +186,8 @@ int32_t bpm_tapper_app(void* p) {
// setup
bpm_state_init(bpm_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, bpm_state, sizeof(bpm_state))) {
+ bpm_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!bpm_state->mutex) {
FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n");
free(bpm_state);
return 255;
@@ -197,7 +198,7 @@ int32_t bpm_tapper_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, bpm_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -207,7 +208,7 @@ int32_t bpm_tapper_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- BPMTapper* bpm_state = (BPMTapper*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(bpm_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
@@ -240,19 +241,16 @@ int32_t bpm_tapper_app(void* p) {
}
}
}
- } else {
- FURI_LOG_D("BPM-Tapper", "FuriMessageQueue: event timeout");
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, bpm_state);
+ furi_mutex_release(bpm_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(bpm_state->mutex);
queue* q = bpm_state->tap_queue;
free(q);
free(bpm_state);
diff --git a/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png
new file mode 100644
index 000000000..089aaed83
Binary files /dev/null and b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png differ
diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam
index 6e2b6d1f9..716c44a6a 100644
--- a/applications/plugins/brainfuck/application.fam
+++ b/applications/plugins/brainfuck/application.fam
@@ -11,5 +11,4 @@ App(
fap_icon="bfico.png",
fap_category="Misc",
fap_icon_assets="icons",
- fap_icon_assets_symbol="brainfuck",
)
diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h
index d3d27dcbd..3940cecad 100644
--- a/applications/plugins/brainfuck/brainfuck_i.h
+++ b/applications/plugins/brainfuck/brainfuck_i.h
@@ -29,7 +29,7 @@ typedef unsigned char byte;
#include
#include
-#include
+#include
#include
#include
diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c
index c5f194500..241a4ebc3 100644
--- a/applications/plugins/brainfuck/views/bf_dev_env.c
+++ b/applications/plugins/brainfuck/views/bf_dev_env.c
@@ -35,9 +35,9 @@ int selectedButton = 0;
int saveNotifyCountdown = 0;
int execCountdown = 0;
-char dspLine0[25] = {};
-char dspLine1[25] = {};
-char dspLine2[25] = {};
+char dspLine0[25] = {0x00};
+char dspLine1[25] = {0x00};
+char dspLine2[25] = {0x00};
static bMapping buttonMappings[12] = {
{8, 8, 7, 1}, //0
@@ -361,6 +361,10 @@ static void bf_dev_enter_callback(void* context) {
//read into the buffer
appDev->dataSize = stream_size(stream);
+ if(appDev->dataSize > 2000) {
+ return; //BF file is too large
+ }
+
stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize);
buffered_file_stream_close(stream);
diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c
index 1b05ac3fd..584bb9fb8 100644
--- a/applications/plugins/brainfuck/worker.c
+++ b/applications/plugins/brainfuck/worker.c
@@ -1,4 +1,6 @@
#include "worker.h"
+#include
+#include
bool killswitch = false;
@@ -207,27 +209,40 @@ static const NotificationSequence led_on = {
};
static const NotificationSequence led_off = {
- &message_green_0,
+ &message_blue_0,
NULL,
};
+void input_kill(void* _ctx) {
+ UNUSED(_ctx);
+ killswitch = true;
+}
+
void beginWorker() {
status = 1;
+
+ //redefined from furi_hal_resources.c
+ const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13};
+
while(inst[instPtr] != 0x00) {
if(runOpCount % 500 == 0) {
text_box_set_text(wrkrApp->text_box, workerGetOutput());
notification_message(wrkrApp->notifications, &led_on);
}
+ //status 2 indicates failure
if(status == 2) {
status = 0;
break;
}
- if(killswitch) {
+
+ //read back button directly to avoid weirdness in furi
+ if(killswitch || !furi_hal_gpio_read(&gpio_button_back)) {
status = 0;
killswitch = false;
break;
}
+
switch(inst[instPtr]) {
case '>':
rShift();
diff --git a/applications/plugins/caesarcipher/application.fam b/applications/plugins/caesarcipher/application.fam
index 4f438d2b3..652585de2 100644
--- a/applications/plugins/caesarcipher/application.fam
+++ b/applications/plugins/caesarcipher/application.fam
@@ -1,7 +1,7 @@
App(
appid="Caesar_Cipher",
name="Caesar Cipher",
- apptype=FlipperAppType.EXTERNAL,
+ apptype=FlipperAppType.PLUGIN,
entry_point="caesar_cipher_app",
cdefines=["APP_CAESAR_CIPHER"],
requires=[
diff --git a/applications/plugins/caesarcipher/caesar_cipher.c b/applications/plugins/caesarcipher/caesar_cipher.c
index 9eb93e925..d5c4b2b65 100644
--- a/applications/plugins/caesarcipher/caesar_cipher.c
+++ b/applications/plugins/caesarcipher/caesar_cipher.c
@@ -21,6 +21,7 @@ typedef struct {
} PluginEvent;
typedef struct {
+ FuriMutex* mutex;
ViewDispatcher* view_dispatcher;
TextInput* text_input;
TextBox* text_box;
@@ -58,7 +59,10 @@ static void build_output(char* input, char* output) {
}
static void text_input_callback(void* ctx) {
- CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25);
+ furi_assert(ctx);
+ CaesarState* caesar_state = ctx;
+ furi_mutex_acquire(caesar_state->mutex, FuriWaitForever);
+
FURI_LOG_D("caesar_cipher", "Input text: %s", caesar_state->input);
// this is where we build the output.
string_to_uppercase(caesar_state->input);
@@ -67,13 +71,14 @@ static void text_input_callback(void* ctx) {
text_box_set_text(caesar_state->text_box, caesar_state->output);
view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 1);
- release_mutex((ValueMutex*)ctx, caesar_state);
+ furi_mutex_release(caesar_state->mutex);
}
static bool back_event_callback(void* ctx) {
- const CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25);
+ const CaesarState* caesar_state = ctx;
+ furi_mutex_acquire(caesar_state->mutex, FuriWaitForever);
view_dispatcher_stop(caesar_state->view_dispatcher);
- release_mutex((ValueMutex*)ctx, caesar_state);
+ furi_mutex_release(caesar_state->mutex);
return true;
}
@@ -99,8 +104,8 @@ int32_t caesar_cipher_app() {
FURI_LOG_D("caesar_cipher", "Running caesar_cipher_state_init");
caesar_cipher_state_init(caesar_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, caesar_state, sizeof(CaesarState))) {
+ caesar_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!caesar_state->mutex) {
FURI_LOG_E("caesar_cipher", "cannot create mutex\r\n");
free(caesar_state);
return 255;
@@ -110,7 +115,7 @@ int32_t caesar_cipher_app() {
text_input_set_result_callback(
caesar_state->text_input,
text_input_callback,
- &state_mutex,
+ caesar_state,
caesar_state->input,
TEXT_BUFFER_SIZE,
//clear default text
@@ -135,12 +140,12 @@ int32_t caesar_cipher_app() {
FURI_LOG_D("ceasar_cipher", "starting view dispatcher");
view_dispatcher_set_navigation_event_callback(
caesar_state->view_dispatcher, back_event_callback);
- view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, &state_mutex);
+ view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, caesar_state);
view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 0);
view_dispatcher_run(caesar_state->view_dispatcher);
furi_record_close("gui");
- delete_mutex(&state_mutex);
+ furi_mutex_free(caesar_state->mutex);
caesar_cipher_state_free(caesar_state);
return 0;
diff --git a/applications/plugins/calculator/calculator.c b/applications/plugins/calculator/calculator.c
index b121641b0..1ca1d3a86 100644
--- a/applications/plugins/calculator/calculator.c
+++ b/applications/plugins/calculator/calculator.c
@@ -19,6 +19,7 @@ typedef struct {
} selectedPosition;
typedef struct {
+ FuriMutex* mutex;
selectedPosition position;
//string with the inputted calculator text
char text[20];
@@ -201,8 +202,10 @@ void generate_calculator_layout(Canvas* canvas) {
};
void calculator_draw_callback(Canvas* canvas, void* ctx) {
- const Calculator* calculator_state = acquire_mutex((ValueMutex*)ctx, 25);
- UNUSED(ctx);
+ furi_assert(ctx);
+ const Calculator* calculator_state = ctx;
+ furi_mutex_acquire(calculator_state->mutex, FuriWaitForever);
+
canvas_clear(canvas);
//show selected button
@@ -240,7 +243,7 @@ void calculator_draw_callback(Canvas* canvas, void* ctx) {
//draw cursor
canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1);
- release_mutex((ValueMutex*)ctx, calculator_state);
+ furi_mutex_release(calculator_state->mutex);
}
void calculator_input_callback(InputEvent* input_event, void* ctx) {
@@ -315,8 +318,8 @@ int32_t calculator_app(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
Calculator* calculator_state = malloc(sizeof(Calculator));
- ValueMutex calculator_state_mutex;
- if(!init_mutex(&calculator_state_mutex, calculator_state, sizeof(Calculator))) {
+ calculator_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!calculator_state->mutex) {
//FURI_LOG_E("calculator", "cannot create mutex\r\n");
free(calculator_state);
return -1;
@@ -324,7 +327,7 @@ int32_t calculator_app(void* p) {
// Configure view port
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, calculator_draw_callback, &calculator_state_mutex);
+ view_port_draw_callback_set(view_port, calculator_draw_callback, calculator_state);
view_port_input_callback_set(view_port, calculator_input_callback, event_queue);
view_port_set_orientation(view_port, ViewPortOrientationVertical);
@@ -444,10 +447,12 @@ int32_t calculator_app(void* p) {
}
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
+ furi_mutex_free(calculator_state->mutex);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
+ free(calculator_state);
return 0;
}
diff --git a/applications/plugins/cli_bridge/README.md b/applications/plugins/cli_bridge/README.md
index 446f3b8a9..b6d35fa7f 100644
--- a/applications/plugins/cli_bridge/README.md
+++ b/applications/plugins/cli_bridge/README.md
@@ -13,12 +13,12 @@ git clone https://github.com/ranchordo/flipperzero-cli-bridge ./applications_use
# If everything went well, the built .fap file can be found in ./dist/f7-D/apps/apps/Tools/cli_gui.fap
```
# Usage
-On the flipperzero, you should be able to find a new application (CLI-GUI Bridge) under Applications->Tools. Opening it will result in a text prompt - the prompt for the command line. Enter a suitable command (quickly pressing the back button will input a space) such as `subghz chat [freq in hz, e.g. 310000000]`, etc, then navigate to and press the SAVE key. You should then see the command window. Use Up and Down to scroll, and use Left or Center to get back to the text input prompt. A quick tap of the back key while viewing the console output sends a Ctrl-C to the console.
+On the flipperzero, you should be able to find a new application (CLI-GUI Bridge) under Applications->Tools. Opening it will result in a text prompt - the prompt for the command line. Enter a suitable command (quickly pressing the back button or holding `_` on the keyboard will input a space) such as `subghz chat [freq in hz, e.g. 310000000]`, etc, then navigate to and press the SAVE key. You should then see the command window. Use Up and Down to scroll, and use Left or Center to get back to the text input prompt. A quick tap of the back key while viewing the console output sends a Ctrl-C to the console, and a long press of the left or right keys during text input will navigate back to the console output without executing.
## Exiting the app
Holding and then releasing the back key for at least a second or so (long press) will exit the app normally, meaning that the inner terminal will send Ctrl-C and close. Any sessions will be disconnected.
-Holding and then releasing the OK key for at least a second or so (long press) will exit the app while keeping the terminal open. Terminal output will be cleared the next time you launch the app, but whatever command or session was running previously will be resumed. This is especially handy with subghz chat - exiting the app while keeping the terminal open will not disconnect you from the chat, and the flipper will still vibrate briefly whenever a new message comes in (even if the app is closed).
+Holding and then releasing the OK key while focusing on the console output for at least a second or so (long press) will exit the app while keeping the terminal open. Terminal output will be cleared the next time you launch the app, but whatever command or session was running previously will be resumed. This is especially handy with subghz chat - exiting the app while keeping the terminal open will not disconnect you from the chat, and the flipper will still vibrate briefly whenever a new message comes in (even if the app is closed).
NOTE: USB functionality (qFlipper, normal USB CLI) may not work after running the app (especially after exiting without closing the terminal), simply restart your flipper and all USB functionality will return to normal.
diff --git a/applications/plugins/cli_bridge/cligui_main.c b/applications/plugins/cli_bridge/cligui_main.c
index 137b4a08b..4e7987c89 100644
--- a/applications/plugins/cli_bridge/cligui_main.c
+++ b/applications/plugins/cli_bridge/cligui_main.c
@@ -50,8 +50,10 @@ static void input_callback_wrapper(InputEvent* event, void* context) {
view_dispatcher_stop(app->view_dispatcher);
}
if(event->type == InputTypeLong && event->key == InputKeyOk) {
- persistent_exit = true;
- view_dispatcher_stop(app->view_dispatcher);
+ if(app->data->state == ViewConsoleOutput) {
+ persistent_exit = true;
+ view_dispatcher_stop(app->view_dispatcher);
+ }
}
if(app->data->state == ViewTextInput) {
text_input_input_handler(app, event);
diff --git a/applications/plugins/cli_bridge/text_input.c b/applications/plugins/cli_bridge/text_input.c
index 295e7629d..568b8ebcd 100644
--- a/applications/plugins/cli_bridge/text_input.c
+++ b/applications/plugins/cli_bridge/text_input.c
@@ -19,8 +19,6 @@ void text_input_result_callback(void* ctx) {
}
void text_input_input_handler(CliguiApp* app, InputEvent* event) {
- UNUSED(app);
- UNUSED(event);
if(event->type == InputTypeShort && event->key == InputKeyBack) {
// view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput);
// app->data->state = ViewConsoleOutput;
@@ -28,4 +26,9 @@ void text_input_input_handler(CliguiApp* app, InputEvent* event) {
app->text_input_store[len] = ' ';
app->text_input_store[len + 1] = 0;
}
+ if(event->type == InputTypeLong &&
+ (event->key == InputKeyLeft || event->key == InputKeyRight)) {
+ view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput);
+ app->data->state = ViewConsoleOutput;
+ }
}
\ No newline at end of file
diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam
deleted file mode 100644
index a6a2eff3e..000000000
--- a/applications/plugins/clock_app/application.fam
+++ /dev/null
@@ -1,10 +0,0 @@
-App(
- appid="clock",
- name="Clock",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="clock_app",
- requires=["gui"],
- stack_size=2 * 1024,
- fap_icon="clock.png",
- fap_category="Tools",
-)
diff --git a/applications/plugins/clock_app/clock.png b/applications/plugins/clock_app/clock.png
deleted file mode 100644
index 0d96df102..000000000
Binary files a/applications/plugins/clock_app/clock.png and /dev/null differ
diff --git a/applications/plugins/clock_app/clock_app.c b/applications/plugins/clock_app/clock_app.c
deleted file mode 100644
index d2c178903..000000000
--- a/applications/plugins/clock_app/clock_app.c
+++ /dev/null
@@ -1,241 +0,0 @@
-#include
-#include
-
-#include
-#include
-
-#include "clock_app.h"
-
-static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
- furi_assert(event_queue);
- PluginEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
-
-static void clock_render_callback(Canvas* const canvas, void* ctx) {
- //canvas_clear(canvas);
- //canvas_set_color(canvas, ColorBlack);
-
- ClockState* state = ctx;
- if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) {
- //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render");
- PluginEvent event = {.type = EventTypeTick};
- furi_message_queue_put(state->event_queue, &event, 0);
- return;
- }
-
- FuriHalRtcDateTime curr_dt;
- furi_hal_rtc_get_datetime(&curr_dt);
- uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
-
- char time_string[TIME_LEN];
- char date_string[DATE_LEN];
- char meridian_string[MERIDIAN_LEN];
- char timer_string[20];
-
- if(state->time_format == LocaleTimeFormat24h) {
- snprintf(
- time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second);
- } else {
- bool pm = curr_dt.hour > 12;
- bool pm12 = curr_dt.hour >= 12;
- bool am12 = curr_dt.hour == 0;
- snprintf(
- time_string,
- TIME_LEN,
- CLOCK_TIME_FORMAT,
- pm ? curr_dt.hour - 12 : (am12 ? 12 : curr_dt.hour),
- curr_dt.minute,
- curr_dt.second);
-
- snprintf(
- meridian_string,
- MERIDIAN_LEN,
- MERIDIAN_FORMAT,
- pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM);
- }
-
- if(state->date_format == LocaleDateFormatYMD) {
- snprintf(
- date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day);
- } else if(state->date_format == LocaleDateFormatMDY) {
- snprintf(
- date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year);
- } else {
- snprintf(
- date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year);
- }
-
- bool timer_running = state->timer_running;
- uint32_t timer_start_timestamp = state->timer_start_timestamp;
- uint32_t timer_stopped_seconds = state->timer_stopped_seconds;
-
- furi_mutex_release(state->mutex);
-
- canvas_set_font(canvas, FontBigNumbers);
-
- if(timer_start_timestamp != 0) {
- int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) :
- timer_stopped_seconds;
- snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60);
- canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME
- canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE
- elements_button_left(canvas, "Reset");
- } else {
- canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, time_string);
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, date_string);
-
- if(state->time_format == LocaleTimeFormat12h)
- canvas_draw_str_aligned(canvas, 65, 12, AlignCenter, AlignCenter, meridian_string);
- }
- if(timer_running) {
- elements_button_center(canvas, "Stop");
- } else if(timer_start_timestamp != 0 && !timer_running) {
- elements_button_center(canvas, "Start");
- }
-}
-
-static void clock_state_init(ClockState* const state) {
- state->time_format = locale_get_time_format();
-
- state->date_format = locale_get_date_format();
-
- //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h");
- //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322");
- //furi_hal_rtc_get_datetime(&state->datetime);
-}
-
-// Runs every 1000ms by default
-static void clock_tick(void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- PluginEvent event = {.type = EventTypeTick};
- // It's OK to loose this event if system overloaded
- furi_message_queue_put(event_queue, &event, 0);
-}
-
-int32_t clock_app(void* p) {
- UNUSED(p);
- ClockState* plugin_state = malloc(sizeof(ClockState));
-
- plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
- if(plugin_state->event_queue == NULL) {
- FURI_LOG_E(TAG, "Cannot create event queue");
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Event queue created");
-
- plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(plugin_state->mutex == NULL) {
- FURI_LOG_E(TAG, "Cannot create mutex");
- furi_message_queue_free(plugin_state->event_queue);
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Mutex created");
-
- clock_state_init(plugin_state);
-
- // Set system callbacks
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, clock_render_callback, plugin_state);
- view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue);
-
- FuriTimer* timer =
- furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue);
-
- if(timer == NULL) {
- FURI_LOG_E(TAG, "Cannot create timer");
- furi_mutex_free(plugin_state->mutex);
- furi_message_queue_free(plugin_state->event_queue);
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Timer created");
-
- // Open GUI and register view_port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
- furi_timer_start(timer, furi_kernel_get_tick_frequency());
- //FURI_LOG_D(TAG, "Timer started");
-
- // Main loop
- PluginEvent event;
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100);
-
- if(event_status != FuriStatusOk) continue;
-
- if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue;
- // press events
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) {
- switch(event.input.key) {
- case InputKeyUp:
- case InputKeyDown:
- case InputKeyRight:
- break;
- case InputKeyLeft:
- if(plugin_state->timer_start_timestamp != 0) {
- // Reset seconds
- plugin_state->timer_running = false;
- plugin_state->timer_start_timestamp = 0;
- plugin_state->timer_stopped_seconds = 0;
- }
- break;
- case InputKeyOk:;
- // START/STOP TIMER
-
- FuriHalRtcDateTime curr_dt;
- furi_hal_rtc_get_datetime(&curr_dt);
- uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
-
- if(plugin_state->timer_running) {
- // Update stopped seconds
- plugin_state->timer_stopped_seconds =
- curr_ts - plugin_state->timer_start_timestamp;
- } else {
- if(plugin_state->timer_start_timestamp == 0) {
- // Set starting timestamp if this is first time
- plugin_state->timer_start_timestamp = curr_ts;
- } else {
- // Timer was already running, need to slightly readjust so we don't
- // count the intervening time
- plugin_state->timer_start_timestamp =
- curr_ts - plugin_state->timer_stopped_seconds;
- }
- }
- plugin_state->timer_running = !plugin_state->timer_running;
- break;
- case InputKeyBack:
- // Exit the plugin
- processing = false;
- break;
- default:
- break;
- }
- }
- } /*else if(event.type == EventTypeTick) {
- furi_hal_rtc_get_datetime(&plugin_state->datetime);
- }*/
-
- view_port_update(view_port);
- furi_mutex_release(plugin_state->mutex);
- }
-
- furi_timer_free(timer);
- view_port_enabled_set(view_port, false);
- gui_remove_view_port(gui, view_port);
- furi_record_close(RECORD_GUI);
- view_port_free(view_port);
- furi_message_queue_free(plugin_state->event_queue);
- furi_mutex_free(plugin_state->mutex);
- free(plugin_state);
-
- return 0;
-}
diff --git a/applications/plugins/clock_app/clock_app.h b/applications/plugins/clock_app/clock_app.h
deleted file mode 100644
index 693bdfac0..000000000
--- a/applications/plugins/clock_app/clock_app.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#define TAG "Clock"
-
-#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d"
-#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d"
-#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d"
-
-#define MERIDIAN_FORMAT "%s"
-#define MERIDIAN_STRING_AM "AM"
-#define MERIDIAN_STRING_PM "PM"
-
-#define TIME_LEN 12
-#define DATE_LEN 14
-#define MERIDIAN_LEN 3
-
-typedef enum {
- EventTypeTick,
- EventTypeKey,
-} EventType;
-
-typedef struct {
- EventType type;
- InputEvent input;
-} PluginEvent;
-
-typedef struct {
- LocaleDateFormat date_format;
- LocaleTimeFormat time_format;
- FuriHalRtcDateTime datetime;
- FuriMutex* mutex;
- FuriMessageQueue* event_queue;
- uint32_t timer_start_timestamp;
- uint32_t timer_stopped_seconds;
- bool timer_running;
-} ClockState;
diff --git a/applications/plugins/cntdown_timer/application.fam b/applications/plugins/cntdown_timer/application.fam
index f40ccaa51..ba22fd4bd 100644
--- a/applications/plugins/cntdown_timer/application.fam
+++ b/applications/plugins/cntdown_timer/application.fam
@@ -1,5 +1,7 @@
+# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
+
App(
- appid="Count_Down_Timer",
+ appid="cntdown_tim",
name="Count Down Timer",
apptype=FlipperAppType.EXTERNAL,
entry_point="app_main",
@@ -10,5 +12,5 @@ App(
stack_size=2 * 1024,
order=20,
fap_icon="cntdown_timer.png",
- fap_category="Tools",
+ fap_category="Misc",
)
diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam
index 5d164f20c..8f0147b09 100644
--- a/applications/plugins/counter/application.fam
+++ b/applications/plugins/counter/application.fam
@@ -1,7 +1,7 @@
App(
- appid="Counter",
+ appid="counter",
name="Counter",
- apptype=FlipperAppType.EXTERNAL,
+ apptype=FlipperAppType.PLUGIN,
entry_point="counterapp",
requires=[
"gui",
diff --git a/applications/plugins/counter/counter.c b/applications/plugins/counter/counter.c
index 886ad3398..22fa8cd80 100644
--- a/applications/plugins/counter/counter.c
+++ b/applications/plugins/counter/counter.c
@@ -2,7 +2,7 @@
#include
#include
#include
-#include
+#include
#define MAX_COUNT 99
#define BOXTIME 2
diff --git a/applications/plugins/counter/media/preview.gif b/applications/plugins/counter/media/preview.gif
deleted file mode 100644
index 87098b733..000000000
Binary files a/applications/plugins/counter/media/preview.gif and /dev/null differ
diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam
index 5489c3b19..4dd2e531e 100644
--- a/applications/plugins/dap_link/application.fam
+++ b/applications/plugins/dap_link/application.fam
@@ -1,7 +1,7 @@
App(
- appid="DAP_Link",
- name="DAP Link",
- apptype=FlipperAppType.EXTERNAL,
+ appid="dap_link",
+ name="[GPIO] DAP Link",
+ apptype=FlipperAppType.PLUGIN,
entry_point="dap_link_app",
requires=[
"gui",
@@ -10,7 +10,7 @@ App(
stack_size=4 * 1024,
order=20,
fap_icon="dap_link.png",
- fap_category="Tools",
+ fap_category="GPIO",
fap_private_libs=[
Lib(
name="free-dap",
diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c
index dd684810a..eafb435e7 100644
--- a/applications/plugins/dap_link/dap_link.c
+++ b/applications/plugins/dap_link/dap_link.c
@@ -14,7 +14,7 @@
#include "gui/dap_gui.h"
#include "usb/dap_v2_usb.h"
#include
-#include "DAP_Link_icons.h"
+#include "dap_link_icons.h"
/***************************************************************************/
/****************************** DAP COMMON *********************************/
@@ -524,4 +524,4 @@ int32_t dap_link_app(void* p) {
dap_app_free(app);
return 0;
-}
+}
\ No newline at end of file
diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/plugins/dap_link/gui/views/dap_main_view.c
index 58bfcf316..c5c8f9dff 100644
--- a/applications/plugins/dap_link/gui/views/dap_main_view.c
+++ b/applications/plugins/dap_link/gui/views/dap_main_view.c
@@ -1,5 +1,5 @@
#include "dap_main_view.h"
-#include "DAP_Link_icons.h"
+#include "dap_link_icons.h"
#include
// extern const Icon I_ArrowDownEmpty_12x18;
diff --git a/applications/plugins/doom/doom.c b/applications/plugins/doom/doom.c
index 78a06055c..b6d08536d 100644
--- a/applications/plugins/doom/doom.c
+++ b/applications/plugins/doom/doom.c
@@ -37,6 +37,7 @@ typedef struct {
} PluginEvent;
typedef struct {
+ FuriMutex* mutex;
Player player;
Entity entity[MAX_ENTITIES];
StaticEntity static_entity[MAX_STATIC_ENTITIES];
@@ -770,10 +771,9 @@ void loopIntro(Canvas* const canvas) {
}
static void render_callback(Canvas* const canvas, void* ctx) {
- PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(plugin_state->init) setupDisplay(canvas);
canvas_set_font(canvas, FontPrimary);
@@ -797,7 +797,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
break;
}
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -967,8 +967,8 @@ int32_t doom_app() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
PluginState* plugin_state = malloc(sizeof(PluginState));
doom_state_init(plugin_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("Doom_game", "cannot create mutex\r\n");
furi_record_close(RECORD_NOTIFICATION);
furi_message_queue_free(event_queue);
@@ -980,11 +980,11 @@ int32_t doom_app() {
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 12);
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
//////////////////////////////////
@@ -997,7 +997,7 @@ int32_t doom_app() {
#endif
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
#ifdef SOUND
furi_check(
furi_mutex_acquire(plugin_state->music_instance->model_mutex, FuriWaitForever) ==
@@ -1087,7 +1087,7 @@ int32_t doom_app() {
furi_mutex_release(plugin_state->music_instance->model_mutex);
#endif
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
#ifdef SOUND
music_player_worker_free(plugin_state->music_instance->worker);
@@ -1099,8 +1099,9 @@ int32_t doom_app() {
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
- furi_record_close("gui");
+ furi_record_close(RECORD_GUI);
view_port_free(view_port);
+ furi_mutex_free(plugin_state->mutex);
furi_message_queue_free(event_queue);
free(plugin_state);
return 0;
diff --git a/applications/plugins/dtmf_dolphin/README.md b/applications/plugins/dtmf_dolphin/README.md
index 5c9561f4b..9a65a382a 100644
--- a/applications/plugins/dtmf_dolphin/README.md
+++ b/applications/plugins/dtmf_dolphin/README.md
@@ -1,4 +1,6 @@
-
+
+
+[Original Link](https://github.com/litui/dtmf_dolphin)
## DTMF Dolphin
diff --git a/applications/plugins/dtmf_dolphin/application.fam b/applications/plugins/dtmf_dolphin/application.fam
index 98fbe7363..0727f5f52 100644
--- a/applications/plugins/dtmf_dolphin/application.fam
+++ b/applications/plugins/dtmf_dolphin/application.fam
@@ -1,5 +1,5 @@
App(
- appid="DTMF_Dolphin",
+ appid="dtmf_dolphin",
name="DTMF Dolphin",
apptype=FlipperAppType.EXTERNAL,
entry_point="dtmf_dolphin_app",
diff --git a/applications/plugins/dtmf_dolphin/assets/dialer.jpg b/applications/plugins/dtmf_dolphin/pics/dialer.jpg
similarity index 100%
rename from applications/plugins/dtmf_dolphin/assets/dialer.jpg
rename to applications/plugins/dtmf_dolphin/pics/dialer.jpg
diff --git a/applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h b/applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h
similarity index 100%
rename from applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h
rename to applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h
diff --git a/applications/plugins/wifi_deauther_v1/application.fam b/applications/plugins/esp8266_deauth/application.fam
similarity index 91%
rename from applications/plugins/wifi_deauther_v1/application.fam
rename to applications/plugins/esp8266_deauth/application.fam
index 6ce0f4435..b289bc88e 100644
--- a/applications/plugins/wifi_deauther_v1/application.fam
+++ b/applications/plugins/esp8266_deauth/application.fam
@@ -8,5 +8,5 @@ App(
stack_size=2 * 1024,
order=20,
fap_icon="wifi_10px.png",
- fap_category="GPIO",
+ fap_category="WiFi",
)
diff --git a/applications/plugins/wifi_deauther_v1/esp8266_deauth.c b/applications/plugins/esp8266_deauth/esp8266_deauth.c
similarity index 95%
rename from applications/plugins/wifi_deauther_v1/esp8266_deauth.c
rename to applications/plugins/esp8266_deauth/esp8266_deauth.c
index 3cc61a588..d32ca4c18 100644
--- a/applications/plugins/wifi_deauther_v1/esp8266_deauth.c
+++ b/applications/plugins/esp8266_deauth/esp8266_deauth.c
@@ -62,6 +62,7 @@ typedef struct SGpioButtons {
} SGpioButtons;
typedef struct SWiFiDeauthApp {
+ FuriMutex* mutex;
Gui* m_gui;
FuriThread* m_worker_thread;
//NotificationApp* m_notification;
@@ -121,10 +122,9 @@ static void esp8266_deauth_app_init(SWiFiDeauthApp* const app) {
}
static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ctx) {
- SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)ctx, 25);
- if(app == NULL) {
- return;
- }
+ furi_assert(ctx);
+ SWiFiDeauthApp* app = ctx;
+ furi_mutex_acquire(app->mutex, FuriWaitForever);
//if(app->m_needUpdateGUI)
//{
@@ -206,7 +206,7 @@ static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ct
break;
}
- release_mutex((ValueMutex*)ctx, app);
+ furi_mutex_release(app->mutex);
}
static void
@@ -235,14 +235,15 @@ static int32_t uart_worker(void* context) {
furi_assert(context);
DEAUTH_APP_LOG_I("[UART] Worker thread init");
- SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25);
+ SWiFiDeauthApp* app = context;
+ furi_mutex_acquire(app->mutex, FuriWaitForever);
if(app == NULL) {
return 1;
}
FuriStreamBuffer* rx_stream = app->m_rx_stream;
- release_mutex((ValueMutex*)context, app);
+ furi_mutex_release(app->mutex);
#if ENABLE_MODULE_POWER
bool initialized = false;
@@ -259,7 +260,8 @@ static int32_t uart_worker(void* context) {
if(events & WorkerEventStop) break;
if(events & WorkerEventRx) {
DEAUTH_APP_LOG_I("[UART] Received data");
- SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25);
+ SWiFiDeauthApp* app = context;
+ furi_mutex_acquire(app->mutex, FuriWaitForever);
if(app == NULL) {
return 1;
}
@@ -307,7 +309,7 @@ static int32_t uart_worker(void* context) {
}
#endif // ENABLE_MODULE_POWER
- release_mutex((ValueMutex*)context, app);
+ furi_mutex_release(app->mutex);
}
}
@@ -356,8 +358,8 @@ int32_t esp8266_deauth_app(void* p) {
#endif
#endif // ENABLE_MODULE_DETECTION
- ValueMutex app_data_mutex;
- if(!init_mutex(&app_data_mutex, app, sizeof(SWiFiDeauthApp))) {
+ app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!app->mutex) {
DEAUTH_APP_LOG_E("cannot create mutex\r\n");
free(app);
return 255;
@@ -365,10 +367,10 @@ int32_t esp8266_deauth_app(void* p) {
DEAUTH_APP_LOG_I("Mutex created");
- //app->m_notification = furi_record_open("notification");
+ //app->m_notification = furi_record_open(RECORD_NOTIFICATION);
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, &app_data_mutex);
+ view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, app);
view_port_input_callback_set(view_port, esp8266_deauth_module_input_callback, event_queue);
// Open GUI and register view_port
@@ -382,7 +384,7 @@ int32_t esp8266_deauth_app(void* p) {
app->m_worker_thread = furi_thread_alloc();
furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker");
furi_thread_set_stack_size(app->m_worker_thread, 1 * 1024);
- furi_thread_set_context(app->m_worker_thread, &app_data_mutex);
+ furi_thread_set_context(app->m_worker_thread, app);
furi_thread_set_callback(app->m_worker_thread, uart_worker);
furi_thread_start(app->m_worker_thread);
DEAUTH_APP_LOG_I("UART thread allocated");
@@ -398,7 +400,7 @@ int32_t esp8266_deauth_app(void* p) {
SPluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- SWiFiDeauthApp* app = (SWiFiDeauthApp*)acquire_mutex_block(&app_data_mutex);
+ furi_mutex_acquire(app->mutex, FuriWaitForever);
#if ENABLE_MODULE_DETECTION
if(!app->m_wifiDeauthModuleAttached) {
@@ -484,7 +486,7 @@ int32_t esp8266_deauth_app(void* p) {
#endif
view_port_update(view_port);
- release_mutex(&app_data_mutex, app);
+ furi_mutex_release(app->mutex);
}
DEAUTH_APP_LOG_I("Start exit app");
@@ -514,7 +516,7 @@ int32_t esp8266_deauth_app(void* p) {
// Close gui record
furi_record_close(RECORD_GUI);
- furi_record_close("notification");
+ //furi_record_close(RECORD_NOTIFICATION);
app->m_gui = NULL;
view_port_free(view_port);
@@ -523,7 +525,7 @@ int32_t esp8266_deauth_app(void* p) {
furi_stream_buffer_free(app->m_rx_stream);
- delete_mutex(&app_data_mutex);
+ furi_mutex_free(app->mutex);
// Free rest
free(app);
diff --git a/applications/plugins/wifi_deauther_v1/wifi_10px.png b/applications/plugins/esp8266_deauth/wifi_10px.png
similarity index 100%
rename from applications/plugins/wifi_deauther_v1/wifi_10px.png
rename to applications/plugins/esp8266_deauth/wifi_10px.png
diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/plugins/flappy_bird/flappy_bird.c
index a86ddb2bb..0c5cd462e 100644
--- a/applications/plugins/flappy_bird/flappy_bird.c
+++ b/applications/plugins/flappy_bird/flappy_bird.c
@@ -1,5 +1,4 @@
#include
-#include
#include
#include
@@ -60,6 +59,7 @@ typedef struct {
PILAR pilars[FLAPPY_PILAR_MAX];
bool debug;
State state;
+ FuriMutex* mutex;
} GameState;
typedef struct {
@@ -175,10 +175,9 @@ static void flappy_game_flap(GameState* const game_state) {
}
static void flappy_game_render_callback(Canvas* const canvas, void* ctx) {
- const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(game_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const GameState* game_state = ctx;
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -256,17 +255,13 @@ static void flappy_game_render_callback(Canvas* const canvas, void* ctx) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 37, 31, "Game Over");
- if(game_state->points != 0 && game_state->points % 5 == 0) {
- DOLPHIN_DEED(getRandomDeed());
- }
-
canvas_set_font(canvas, FontSecondary);
char buffer[12];
snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points);
canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);
}
- release_mutex((ValueMutex*)ctx, game_state);
+ furi_mutex_release(game_state->mutex);
}
static void flappy_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -292,8 +287,8 @@ int32_t flappy_game_app(void* p) {
GameState* game_state = malloc(sizeof(GameState));
flappy_game_state_init(game_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
+ game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!game_state->mutex) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
@@ -301,7 +296,7 @@ int32_t flappy_game_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, flappy_game_render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, flappy_game_render_callback, game_state);
view_port_input_callback_set(view_port, flappy_game_input_callback, event_queue);
FuriTimer* timer =
@@ -315,7 +310,7 @@ int32_t flappy_game_app(void* p) {
GameEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -357,7 +352,7 @@ int32_t flappy_game_app(void* p) {
}
view_port_update(view_port);
- release_mutex(&state_mutex, game_state);
+ furi_mutex_release(game_state->mutex);
}
furi_timer_free(timer);
@@ -365,7 +360,7 @@ int32_t flappy_game_app(void* p) {
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
- delete_mutex(&state_mutex);
+ furi_mutex_free(game_state->mutex);
free_and_exit:
flappy_game_state_free(game_state);
diff --git a/applications/plugins/flashlight/application.fam b/applications/plugins/flashlight/application.fam
index 6d70a036f..ea8eb03d1 100644
--- a/applications/plugins/flashlight/application.fam
+++ b/applications/plugins/flashlight/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Flashlight",
+ appid="flashlight",
name="[GPIO] Flashlight",
apptype=FlipperAppType.EXTERNAL,
entry_point="flashlight_app",
diff --git a/applications/plugins/flashlight/flashlight.c b/applications/plugins/flashlight/flashlight.c
index 534d48fdb..251c596ef 100644
--- a/applications/plugins/flashlight/flashlight.c
+++ b/applications/plugins/flashlight/flashlight.c
@@ -18,14 +18,14 @@ typedef struct {
} PluginEvent;
typedef struct {
+ FuriMutex* mutex;
bool is_on;
} PluginState;
static void render_callback(Canvas* const canvas, void* ctx) {
- const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight");
@@ -41,7 +41,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off");
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -69,8 +69,8 @@ int32_t flashlight_app() {
PluginState* plugin_state = malloc(sizeof(PluginState));
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("flashlight", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
@@ -79,7 +79,7 @@ int32_t flashlight_app() {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -90,7 +90,7 @@ int32_t flashlight_app() {
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -116,7 +116,7 @@ int32_t flashlight_app() {
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
view_port_enabled_set(view_port, false);
@@ -124,7 +124,7 @@ int32_t flashlight_app() {
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(plugin_state->mutex);
return 0;
}
diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/plugins/flipfrid/flipfrid.c
index c2831f2f6..4f28be7b1 100644
--- a/applications/plugins/flipfrid/flipfrid.c
+++ b/applications/plugins/flipfrid/flipfrid.c
@@ -9,11 +9,9 @@
#define RFIDFUZZER_APP_FOLDER "/ext/lrfid/rfidfuzzer"
static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) {
- FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100);
-
- if(flipfrid_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ FlipFridState* flipfrid_state = ctx;
+ furi_mutex_acquire(flipfrid_state->mutex, FuriWaitForever);
// Draw correct Canvas
switch(flipfrid_state->current_scene) {
@@ -37,7 +35,7 @@ static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) {
break;
}
- release_mutex((ValueMutex*)ctx, flipfrid_state);
+ furi_mutex_release(flipfrid_state->mutex);
}
void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -118,11 +116,9 @@ int32_t flipfrid_start(void* p) {
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent));
FlipFridState* flipfrid_state = flipfrid_alloc();
- ValueMutex flipfrid_state_mutex;
- // Mutex
- FURI_LOG_I(TAG, "Initializing flipfrid mutex");
- if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) {
+ flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!flipfrid_state->mutex) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
@@ -139,7 +135,7 @@ int32_t flipfrid_start(void* p) {
// Configure view port
FURI_LOG_I(TAG, "Initializing viewport");
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex);
+ view_port_draw_callback_set(view_port, flipfrid_draw_callback, flipfrid_state);
view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue);
// Configure timer
@@ -268,6 +264,7 @@ int32_t flipfrid_start(void* p) {
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
+ furi_mutex_free(flipfrid_state->mutex);
flipfrid_free(flipfrid_state);
return 0;
diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/plugins/flipfrid/flipfrid.h
index 6b8662e65..e4122054b 100644
--- a/applications/plugins/flipfrid/flipfrid.h
+++ b/applications/plugins/flipfrid/flipfrid.h
@@ -58,6 +58,7 @@ typedef struct {
// STRUCTS
typedef struct {
+ FuriMutex* mutex;
bool is_running;
bool is_attacking;
FlipFridScene current_scene;
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c
index e2e8b76a2..32157556b 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c
@@ -1,7 +1,6 @@
#include "flipfrid_scene_load_custom_uids.h"
#include "flipfrid_scene_run_attack.h"
#include "flipfrid_scene_entrypoint.h"
-#include "RFID_Fuzzer_icons.h"
#define LFRFID_UIDS_EXTENSION ".txt"
#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer"
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
index ce6a40b11..5f3f1a31b 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
@@ -1,6 +1,5 @@
#include "flipfrid_scene_load_file.h"
#include "flipfrid_scene_entrypoint.h"
-#include "RFID_Fuzzer_icons.h"
#define LFRFID_APP_EXTENSION ".rfid"
#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid"
diff --git a/applications/plugins/flipper_i2ctools/.gitignore b/applications/plugins/flipper_i2ctools/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/applications/plugins/flipper_i2ctools/README.md b/applications/plugins/flipper_i2ctools/README.md
index 8ecf47c8c..5ee1f881e 100644
--- a/applications/plugins/flipper_i2ctools/README.md
+++ b/applications/plugins/flipper_i2ctools/README.md
@@ -1,5 +1,6 @@
# flipperzero-i2ctools
+[Original link](https://github.com/NaejEL/flipperzero-i2ctools)
Set of i2c tools for Flipper Zero

diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam
index f6522a86e..44138691d 100644
--- a/applications/plugins/flipper_i2ctools/application.fam
+++ b/applications/plugins/flipper_i2ctools/application.fam
@@ -1,5 +1,5 @@
App(
- appid="I2C_Tools",
+ appid="i2cTools",
name="[GPIO] i2c Tools",
apptype=FlipperAppType.EXTERNAL,
entry_point="i2ctools_app",
diff --git a/applications/plugins/flipper_i2ctools/i2ctools.c b/applications/plugins/flipper_i2ctools/i2ctools.c
index 9d73a73b8..6d4cc739f 100644
--- a/applications/plugins/flipper_i2ctools/i2ctools.c
+++ b/applications/plugins/flipper_i2ctools/i2ctools.c
@@ -1,7 +1,9 @@
#include "i2ctools_i.h"
void i2ctools_draw_callback(Canvas* canvas, void* ctx) {
- i2cTools* i2ctools = acquire_mutex((ValueMutex*)ctx, 25);
+ furi_assert(ctx);
+ i2cTools* i2ctools = ctx;
+ furi_mutex_acquire(i2ctools->mutex, FuriWaitForever);
switch(i2ctools->main_view->current_view) {
case MAIN_VIEW:
@@ -23,7 +25,7 @@ void i2ctools_draw_callback(Canvas* canvas, void* ctx) {
default:
break;
}
- release_mutex((ValueMutex*)ctx, i2ctools);
+ furi_mutex_release(i2ctools->mutex);
}
void i2ctools_input_callback(InputEvent* input_event, void* ctx) {
@@ -38,8 +40,8 @@ int32_t i2ctools_app(void* p) {
// Alloc i2ctools
i2cTools* i2ctools = malloc(sizeof(i2cTools));
- ValueMutex i2ctools_mutex;
- if(!init_mutex(&i2ctools_mutex, i2ctools, sizeof(i2cTools))) {
+ i2ctools->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!i2ctools->mutex) {
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
free(i2ctools);
return -1;
@@ -47,7 +49,7 @@ int32_t i2ctools_app(void* p) {
// Alloc viewport
i2ctools->view_port = view_port_alloc();
- view_port_draw_callback_set(i2ctools->view_port, i2ctools_draw_callback, &i2ctools_mutex);
+ view_port_draw_callback_set(i2ctools->view_port, i2ctools_draw_callback, i2ctools);
view_port_input_callback_set(i2ctools->view_port, i2ctools_input_callback, event_queue);
// Register view port in GUI
@@ -216,6 +218,7 @@ int32_t i2ctools_app(void* p) {
i2c_scanner_free(i2ctools->scanner);
i2c_sender_free(i2ctools->sender);
i2c_main_view_free(i2ctools->main_view);
+ furi_mutex_free(i2ctools->mutex);
free(i2ctools);
furi_record_close(RECORD_GUI);
return 0;
diff --git a/applications/plugins/flipper_i2ctools/i2ctools_i.h b/applications/plugins/flipper_i2ctools/i2ctools_i.h
index 33917dc34..f31e42478 100644
--- a/applications/plugins/flipper_i2ctools/i2ctools_i.h
+++ b/applications/plugins/flipper_i2ctools/i2ctools_i.h
@@ -13,6 +13,7 @@
// App datas
typedef struct {
+ FuriMutex* mutex;
ViewPort* view_port;
i2cMainView* main_view;
diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/plugins/flipper_i2ctools/views/main_view.h
index dc9379190..050e41130 100644
--- a/applications/plugins/flipper_i2ctools/views/main_view.h
+++ b/applications/plugins/flipper_i2ctools/views/main_view.h
@@ -1,7 +1,7 @@
#include
#include
#include
-#include
+#include
#define APP_NAME "I2C Tools"
#define SCAN_MENU_TEXT "Scan"
diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/plugins/flipper_i2ctools/views/scanner_view.h
index bd591a740..02bc8fb1c 100644
--- a/applications/plugins/flipper_i2ctools/views/scanner_view.h
+++ b/applications/plugins/flipper_i2ctools/views/scanner_view.h
@@ -1,7 +1,7 @@
#include
#include
#include
-#include
+#include
#include "../i2cscanner.h"
#define SCAN_TEXT "SCAN"
diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/plugins/flipper_i2ctools/views/sender_view.h
index 8a21912dc..5f48081dd 100644
--- a/applications/plugins/flipper_i2ctools/views/sender_view.h
+++ b/applications/plugins/flipper_i2ctools/views/sender_view.h
@@ -1,7 +1,7 @@
#include
#include
#include
-#include
+#include
#include "../i2csender.h"
#define SEND_TEXT "SEND"
diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/plugins/flipper_i2ctools/views/sniffer_view.h
index d5cc373d1..80c92f7fc 100644
--- a/applications/plugins/flipper_i2ctools/views/sniffer_view.h
+++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.h
@@ -1,7 +1,7 @@
#include
#include
#include
-#include
+#include
#include "../i2csniffer.h"
#define SNIFF_TEXT "SNIFF"
diff --git a/applications/plugins/game15/application.fam b/applications/plugins/game15/application.fam
index dc3a0da0b..ab00316c1 100644
--- a/applications/plugins/game15/application.fam
+++ b/applications/plugins/game15/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Game15",
+ appid="game15",
name="Game 15",
apptype=FlipperAppType.EXTERNAL,
entry_point="game15_app",
diff --git a/applications/plugins/game15/game15.c b/applications/plugins/game15/game15.c
index d9b059466..8faea4380 100644
--- a/applications/plugins/game15/game15.c
+++ b/applications/plugins/game15/game15.c
@@ -461,6 +461,7 @@ int32_t game15_app() {
sandbox_init(
FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler);
+
sandbox_loop();
sandbox_free();
game_free();
diff --git a/applications/plugins/game_2048/application.fam b/applications/plugins/game_2048/application.fam
index f2456668e..0440d0864 100644
--- a/applications/plugins/game_2048/application.fam
+++ b/applications/plugins/game_2048/application.fam
@@ -1,6 +1,6 @@
# PLUGIN BY eugene-kirzhanov
App(
- appid="2048_improved",
+ appid="game_2048",
name="2048",
apptype=FlipperAppType.EXTERNAL,
entry_point="game_2048_app",
diff --git a/applications/plugins/game_2048/game_2048.c b/applications/plugins/game_2048/game_2048.c
index e0c4a4cec..cf8bba8f1 100644
--- a/applications/plugins/game_2048/game_2048.c
+++ b/applications/plugins/game_2048/game_2048.c
@@ -33,6 +33,7 @@ typedef enum {
} State;
typedef struct {
+ FuriMutex* mutex;
State state;
uint8_t table[CELLS_COUNT][CELLS_COUNT];
uint32_t score;
@@ -103,8 +104,9 @@ static void gray_canvas(Canvas* const canvas) {
}
static void draw_callback(Canvas* const canvas, void* ctx) {
- const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(game_state == NULL) return;
+ furi_assert(ctx);
+ const GameState* game_state = ctx;
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
canvas_clear(canvas);
@@ -180,7 +182,7 @@ static void draw_callback(Canvas* const canvas, void* ctx) {
canvas_draw_str_aligned(canvas, 64, 48, AlignCenter, AlignBottom, buf);
}
- release_mutex((ValueMutex*)ctx, game_state);
+ furi_mutex_release(game_state->mutex);
}
void calculate_move_to_left(uint8_t arr[], MoveResult* const move_result) {
@@ -380,9 +382,9 @@ int32_t game_2048_app() {
MoveResult* move_result = malloc(sizeof(MoveResult));
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
- FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
+ game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!game_state->mutex) {
+ FURI_LOG_E("2048Game", "cannot create mutex\r\n");
free(game_state);
return 255;
}
@@ -391,7 +393,7 @@ int32_t game_2048_app() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, draw_callback, game_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
@@ -404,7 +406,7 @@ int32_t game_2048_app() {
// handle only press event, ignore repeat/release events
if(input.type != InputTypePress) continue;
- GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(game_state->mutex, FuriWaitForever);
switch(game_state->state) {
case GameStateMenu:
@@ -489,7 +491,7 @@ int32_t game_2048_app() {
}
view_port_update(view_port);
- release_mutex(&state_mutex, game_state);
+ furi_mutex_release(game_state->mutex);
}
}
@@ -500,7 +502,7 @@ int32_t game_2048_app() {
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(game_state->mutex);
free(game_state);
free(move_result);
diff --git a/applications/plugins/game_of_life/game_of_life.c b/applications/plugins/game_of_life/game_of_life.c
index 65be8cb72..8629062b8 100644
--- a/applications/plugins/game_of_life/game_of_life.c
+++ b/applications/plugins/game_of_life/game_of_life.c
@@ -21,6 +21,7 @@ typedef struct {
typedef struct {
bool revive;
int evo;
+ FuriMutex* mutex;
} State;
unsigned char new[TOTAL_PIXELS] = {};
@@ -92,7 +93,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
}
static void render_callback(Canvas* canvas, void* ctx) {
- State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25);
+ //furi_assert(ctx);
+ State* state = ctx;
+ furi_mutex_acquire(state->mutex, FuriWaitForever);
+
canvas_clear(canvas);
for(int i = 0; i < TOTAL_PIXELS; ++i) {
@@ -100,7 +104,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
int y = (int)(i / SCREEN_WIDTH);
if(fields[current][i] == 1) canvas_draw_dot(canvas, x, y);
}
- release_mutex((ValueMutex*)ctx, state);
+ furi_mutex_release(state->mutex);
}
int32_t game_of_life_app(void* p) {
@@ -112,8 +116,8 @@ int32_t game_of_life_app(void* p) {
State* _state = malloc(sizeof(State));
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, _state, sizeof(State))) {
+ _state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!_state->mutex) {
printf("cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(_state);
@@ -121,7 +125,7 @@ int32_t game_of_life_app(void* p) {
}
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, _state);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
@@ -129,23 +133,23 @@ int32_t game_of_life_app(void* p) {
AppEvent event;
for(bool processing = true; processing;) {
- State* state = (State*)acquire_mutex_block(&state_mutex);
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
+ furi_mutex_acquire(_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk && event.type == EventTypeKey &&
event.input.type == InputTypePress) {
if(event.input.key == InputKeyBack) {
// furiac_exit(NULL);
processing = false;
- release_mutex(&state_mutex, state);
+ furi_mutex_release(_state->mutex);
break;
}
}
- update_field(state);
+ update_field(_state);
view_port_update(view_port);
- release_mutex(&state_mutex, state);
+ furi_mutex_release(_state->mutex);
}
view_port_enabled_set(view_port, false);
@@ -153,7 +157,7 @@ int32_t game_of_life_app(void* p) {
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(_state->mutex);
free(_state);
return 0;
diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam
index 71be5a161..ea5e1fa85 100644
--- a/applications/plugins/geigercounter/application.fam
+++ b/applications/plugins/geigercounter/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Geiger_Coutner",
+ appid="flipper_geiger",
name="[GPIO] Geiger Counter",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipper_geiger_app",
diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/plugins/geigercounter/flipper_geiger.c
index 709db9a26..a5503eb90 100644
--- a/applications/plugins/geigercounter/flipper_geiger.c
+++ b/applications/plugins/geigercounter/flipper_geiger.c
@@ -29,6 +29,7 @@ typedef struct {
} EventApp;
typedef struct {
+ FuriMutex* mutex;
uint32_t cps, cpm;
uint32_t line[SCREEN_SIZE_X / 2];
float coef;
@@ -36,12 +37,13 @@ typedef struct {
} mutexStruct;
static void draw_callback(Canvas* canvas, void* ctx) {
- UNUSED(ctx);
+ furi_assert(ctx);
mutexStruct displayStruct;
- mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx);
+ mutexStruct* geigerMutex = ctx;
+ furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever);
memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct));
- release_mutex((ValueMutex*)ctx, geigerMutex);
+ furi_mutex_release(geigerMutex->mutex);
char buffer[32];
if(displayStruct.data == 0)
@@ -101,7 +103,8 @@ static void gpiocallback(void* ctx) {
furi_message_queue_put(queue, &event, 0);
}
-int32_t flipper_geiger_app() {
+int32_t flipper_geiger_app(void* p) {
+ UNUSED(p);
EventApp event;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
@@ -117,11 +120,14 @@ int32_t flipper_geiger_app() {
uint32_t counter = 0;
- ValueMutex state_mutex;
- init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal));
+ mutexVal.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!mutexVal.mutex) {
+ furi_message_queue_free(event_queue);
+ return 255;
+ }
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, draw_callback, &mutexVal);
view_port_input_callback_set(view_port, input_callback, event_queue);
furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
@@ -146,62 +152,62 @@ int32_t flipper_geiger_app() {
break;
} else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) {
counter = 0;
- mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
- geigerMutex->cps = 0;
- geigerMutex->cpm = 0;
- for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0;
+ mutexVal.cps = 0;
+ mutexVal.cpm = 0;
+ for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0;
screenRefresh = 1;
- release_mutex(&state_mutex, geigerMutex);
+ furi_mutex_release(mutexVal.mutex);
} else if((event.input.key == InputKeyLeft &&
event.input.type == InputTypeShort)) {
- mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
- if(geigerMutex->data != 0)
- geigerMutex->data--;
+ if(mutexVal.data != 0)
+ mutexVal.data--;
else
- geigerMutex->data = 2;
+ mutexVal.data = 2;
screenRefresh = 1;
- release_mutex(&state_mutex, geigerMutex);
+ furi_mutex_release(mutexVal.mutex);
} else if((event.input.key == InputKeyRight &&
event.input.type == InputTypeShort)) {
- mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
- if(geigerMutex->data != 2)
- geigerMutex->data++;
+ if(mutexVal.data != 2)
+ mutexVal.data++;
else
- geigerMutex->data = 0;
+ mutexVal.data = 0;
screenRefresh = 1;
- release_mutex(&state_mutex, geigerMutex);
+ furi_mutex_release(mutexVal.mutex);
}
} else if(event.type == ClockEventTypeTick) {
- mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++)
- geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] =
- geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i];
+ mutexVal.line[SCREEN_SIZE_X / 2 - 1 - i] =
+ mutexVal.line[SCREEN_SIZE_X / 2 - 2 - i];
- geigerMutex->line[0] = counter;
- geigerMutex->cps = counter;
+ mutexVal.line[0] = counter;
+ mutexVal.cps = counter;
counter = 0;
- geigerMutex->cpm = geigerMutex->line[0];
- uint32_t max = geigerMutex->line[0];
+ mutexVal.cpm = mutexVal.line[0];
+ uint32_t max = mutexVal.line[0];
for(int i = 1; i < SCREEN_SIZE_X / 2; i++) {
- if(i < 60) geigerMutex->cpm += geigerMutex->line[i];
- if(geigerMutex->line[i] > max) max = geigerMutex->line[i];
+ if(i < 60) mutexVal.cpm += mutexVal.line[i];
+ if(mutexVal.line[i] > max) max = mutexVal.line[i];
}
if(max > 0)
- geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max);
+ mutexVal.coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max);
else
- geigerMutex->coef = 1;
+ mutexVal.coef = 1;
screenRefresh = 1;
- release_mutex(&state_mutex, geigerMutex);
+ furi_mutex_release(mutexVal.mutex);
} else if(event.type == EventGPIO) {
counter++;
}
@@ -217,7 +223,7 @@ int32_t flipper_geiger_app() {
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(mutexVal.mutex);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_timer_free(timer);
diff --git a/applications/plugins/gpioreader2/GPIO_reader.c b/applications/plugins/gpio_reader_a/GPIO_reader.c
similarity index 88%
rename from applications/plugins/gpioreader2/GPIO_reader.c
rename to applications/plugins/gpio_reader_a/GPIO_reader.c
index be333cd7e..eb5f95e67 100644
--- a/applications/plugins/gpioreader2/GPIO_reader.c
+++ b/applications/plugins/gpio_reader_a/GPIO_reader.c
@@ -17,10 +17,13 @@ typedef struct {
typedef struct {
int pin;
int pullMode;
+ FuriMutex* mutex;
} PluginState;
static void render_callback(Canvas* const canvas, void* ctx) {
- const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
@@ -57,7 +60,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
AlignCenter,
gpio_item_get_pin_level(plugin_state->pin));
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -79,8 +82,8 @@ int32_t GPIO_reader_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
GPIO_reader_state_init(plugin_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("GPIO_reader", "cannot create mutex\r\n");
free(plugin_state);
return 255;
@@ -88,7 +91,7 @@ int32_t GPIO_reader_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -98,7 +101,7 @@ int32_t GPIO_reader_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -131,20 +134,19 @@ int32_t GPIO_reader_app(void* p) {
}
}
}
- } else {
- FURI_LOG_D("GPIO_reader", "FuriMessageQueue: event timeout");
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
+ furi_mutex_free(plugin_state->mutex);
furi_message_queue_free(event_queue);
+ free(plugin_state);
return 0;
}
\ No newline at end of file
diff --git a/applications/plugins/gpioreader2/GPIO_reader_item.c b/applications/plugins/gpio_reader_a/GPIO_reader_item.c
similarity index 100%
rename from applications/plugins/gpioreader2/GPIO_reader_item.c
rename to applications/plugins/gpio_reader_a/GPIO_reader_item.c
diff --git a/applications/plugins/gpioreader2/GPIO_reader_item.h b/applications/plugins/gpio_reader_a/GPIO_reader_item.h
similarity index 100%
rename from applications/plugins/gpioreader2/GPIO_reader_item.h
rename to applications/plugins/gpio_reader_a/GPIO_reader_item.h
diff --git a/applications/plugins/gpioreader2/README.md b/applications/plugins/gpio_reader_a/README.md
similarity index 100%
rename from applications/plugins/gpioreader2/README.md
rename to applications/plugins/gpio_reader_a/README.md
diff --git a/applications/plugins/gpioreader2/application.fam b/applications/plugins/gpio_reader_a/application.fam
similarity index 89%
rename from applications/plugins/gpioreader2/application.fam
rename to applications/plugins/gpio_reader_a/application.fam
index bf25c323d..4ec90df6a 100644
--- a/applications/plugins/gpioreader2/application.fam
+++ b/applications/plugins/gpio_reader_a/application.fam
@@ -1,5 +1,5 @@
App(
- appid="GPIO_Reader_A",
+ appid="gpio_reader_a",
name="[GPIO] Reader (Aurelilc)",
apptype=FlipperAppType.EXTERNAL,
entry_point="GPIO_reader_app",
diff --git a/applications/plugins/gpioreader/icon.png b/applications/plugins/gpio_reader_a/icon.png
similarity index 100%
rename from applications/plugins/gpioreader/icon.png
rename to applications/plugins/gpio_reader_a/icon.png
diff --git a/applications/plugins/gpioreader/LICENSE b/applications/plugins/gpio_reader_b/LICENSE
similarity index 100%
rename from applications/plugins/gpioreader/LICENSE
rename to applications/plugins/gpio_reader_b/LICENSE
diff --git a/applications/plugins/gpioreader/README.md b/applications/plugins/gpio_reader_b/README.md
similarity index 100%
rename from applications/plugins/gpioreader/README.md
rename to applications/plugins/gpio_reader_b/README.md
diff --git a/applications/plugins/gpioreader/application.fam b/applications/plugins/gpio_reader_b/application.fam
similarity index 91%
rename from applications/plugins/gpioreader/application.fam
rename to applications/plugins/gpio_reader_b/application.fam
index a7f297d47..aa26060aa 100644
--- a/applications/plugins/gpioreader/application.fam
+++ b/applications/plugins/gpio_reader_b/application.fam
@@ -1,5 +1,5 @@
App(
- appid="GPIO_Reader_B",
+ appid="gpio_reader_b",
name="[GPIO] Reader (biotinker)",
apptype=FlipperAppType.EXTERNAL,
entry_point="gpio_app",
diff --git a/applications/plugins/gpioreader/gpio_app.c b/applications/plugins/gpio_reader_b/gpio_app.c
similarity index 100%
rename from applications/plugins/gpioreader/gpio_app.c
rename to applications/plugins/gpio_reader_b/gpio_app.c
diff --git a/applications/plugins/gpioreader/gpio_app.h b/applications/plugins/gpio_reader_b/gpio_app.h
similarity index 100%
rename from applications/plugins/gpioreader/gpio_app.h
rename to applications/plugins/gpio_reader_b/gpio_app.h
diff --git a/applications/plugins/gpioreader/gpio_app_i.h b/applications/plugins/gpio_reader_b/gpio_app_i.h
similarity index 100%
rename from applications/plugins/gpioreader/gpio_app_i.h
rename to applications/plugins/gpio_reader_b/gpio_app_i.h
diff --git a/applications/plugins/gpioreader/gpio_custom_event.h b/applications/plugins/gpio_reader_b/gpio_custom_event.h
similarity index 100%
rename from applications/plugins/gpioreader/gpio_custom_event.h
rename to applications/plugins/gpio_reader_b/gpio_custom_event.h
diff --git a/applications/plugins/gpioreader/gpio_item.c b/applications/plugins/gpio_reader_b/gpio_item.c
similarity index 100%
rename from applications/plugins/gpioreader/gpio_item.c
rename to applications/plugins/gpio_reader_b/gpio_item.c
diff --git a/applications/plugins/gpioreader/gpio_item.h b/applications/plugins/gpio_reader_b/gpio_item.h
similarity index 100%
rename from applications/plugins/gpioreader/gpio_item.h
rename to applications/plugins/gpio_reader_b/gpio_item.h
diff --git a/applications/plugins/gpioreader/gpioreader.png b/applications/plugins/gpio_reader_b/gpioreader.png
similarity index 100%
rename from applications/plugins/gpioreader/gpioreader.png
rename to applications/plugins/gpio_reader_b/gpioreader.png
diff --git a/applications/plugins/gpioreader2/icon.png b/applications/plugins/gpio_reader_b/icon.png
similarity index 100%
rename from applications/plugins/gpioreader2/icon.png
rename to applications/plugins/gpio_reader_b/icon.png
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene.h b/applications/plugins/gpio_reader_b/scenes/gpio_scene.h
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene.h
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene.h
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_config.h b/applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_config.h
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_reader.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_reader.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_start.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_start.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_test.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_test.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c
diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_config.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c
similarity index 100%
rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_config.c
rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c
diff --git a/applications/plugins/gpioreader/usb_uart_bridge.c b/applications/plugins/gpio_reader_b/usb_uart_bridge.c
similarity index 100%
rename from applications/plugins/gpioreader/usb_uart_bridge.c
rename to applications/plugins/gpio_reader_b/usb_uart_bridge.c
diff --git a/applications/plugins/gpioreader/usb_uart_bridge.h b/applications/plugins/gpio_reader_b/usb_uart_bridge.h
similarity index 100%
rename from applications/plugins/gpioreader/usb_uart_bridge.h
rename to applications/plugins/gpio_reader_b/usb_uart_bridge.h
diff --git a/applications/plugins/gpioreader/views/gpio_reader.c b/applications/plugins/gpio_reader_b/views/gpio_reader.c
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_reader.c
rename to applications/plugins/gpio_reader_b/views/gpio_reader.c
diff --git a/applications/plugins/gpioreader/views/gpio_reader.h b/applications/plugins/gpio_reader_b/views/gpio_reader.h
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_reader.h
rename to applications/plugins/gpio_reader_b/views/gpio_reader.h
diff --git a/applications/plugins/gpioreader/views/gpio_test.c b/applications/plugins/gpio_reader_b/views/gpio_test.c
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_test.c
rename to applications/plugins/gpio_reader_b/views/gpio_test.c
diff --git a/applications/plugins/gpioreader/views/gpio_test.h b/applications/plugins/gpio_reader_b/views/gpio_test.h
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_test.h
rename to applications/plugins/gpio_reader_b/views/gpio_test.h
diff --git a/applications/plugins/gpioreader/views/gpio_usb_uart.c b/applications/plugins/gpio_reader_b/views/gpio_usb_uart.c
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_usb_uart.c
rename to applications/plugins/gpio_reader_b/views/gpio_usb_uart.c
diff --git a/applications/plugins/gpioreader/views/gpio_usb_uart.h b/applications/plugins/gpio_reader_b/views/gpio_usb_uart.h
similarity index 100%
rename from applications/plugins/gpioreader/views/gpio_usb_uart.h
rename to applications/plugins/gpio_reader_b/views/gpio_usb_uart.h
diff --git a/applications/plugins/gps_nmea_uart/README.md b/applications/plugins/gps_nmea_uart/README.md
index 3f7a5ef3d..7af0fa3b3 100644
--- a/applications/plugins/gps_nmea_uart/README.md
+++ b/applications/plugins/gps_nmea_uart/README.md
@@ -1,12 +1,9 @@
# GPS for Flipper Zero
-A simple Flipper Zero application for NMEA 0183 serial GPS modules, such as the
-
[Original link](https://github.com/ezod/flipperzero-gps)
+
[Adafruit Ultimate GPS Breakout].
-[Original link](https://github.com/ezod/flipperzero-gps)
-

Heavy lifting (NMEA parsing) provided by [minmea], which is included in this
@@ -35,4 +32,3 @@ baud rate, may be useful for other GPS modules.
[Adafruit Ultimate GPS Breakout]: https://www.adafruit.com/product/746
[minmea]: https://github.com/kosma/minmea
[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware
-[qFlipper]: https://flipperzero.one/update
diff --git a/applications/plugins/gps_nmea_uart/application.fam b/applications/plugins/gps_nmea_uart/application.fam
index 138fb3f29..a5fdb4360 100644
--- a/applications/plugins/gps_nmea_uart/application.fam
+++ b/applications/plugins/gps_nmea_uart/application.fam
@@ -1,5 +1,5 @@
App(
- appid="NMEA_GPS",
+ appid="gps_nmea",
name="[NMEA] GPS",
apptype=FlipperAppType.EXTERNAL,
entry_point="gps_app",
diff --git a/applications/plugins/gps_nmea_uart/gps.c b/applications/plugins/gps_nmea_uart/gps.c
index 62053cede..b36fd080b 100644
--- a/applications/plugins/gps_nmea_uart/gps.c
+++ b/applications/plugins/gps_nmea_uart/gps.c
@@ -15,10 +15,9 @@ typedef struct {
} PluginEvent;
static void render_callback(Canvas* const canvas, void* context) {
- const GpsUart* gps_uart = acquire_mutex((ValueMutex*)context, 25);
- if(gps_uart == NULL) {
- return;
- }
+ furi_assert(context);
+ const GpsUart* gps_uart = context;
+ furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude");
@@ -57,7 +56,7 @@ static void render_callback(Canvas* const canvas, void* context) {
gps_uart->status.time_seconds);
canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer);
- release_mutex((ValueMutex*)context, gps_uart);
+ furi_mutex_release(gps_uart->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -74,8 +73,8 @@ int32_t gps_app(void* p) {
GpsUart* gps_uart = gps_uart_enable();
- ValueMutex gps_uart_mutex;
- if(!init_mutex(&gps_uart_mutex, gps_uart, sizeof(GpsUart))) {
+ gps_uart->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!gps_uart->mutex) {
FURI_LOG_E("GPS", "cannot create mutex\r\n");
free(gps_uart);
return 255;
@@ -83,18 +82,18 @@ int32_t gps_app(void* p) {
// set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &gps_uart_mutex);
+ view_port_draw_callback_set(view_port, render_callback, gps_uart);
view_port_input_callback_set(view_port, input_callback, event_queue);
// open GUI and register view_port
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- GpsUart* gps_uart = (GpsUart*)acquire_mutex_block(&gps_uart_mutex);
+ furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -118,15 +117,15 @@ int32_t gps_app(void* p) {
}
view_port_update(view_port);
- release_mutex(&gps_uart_mutex, gps_uart);
+ furi_mutex_release(gps_uart->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
- furi_record_close("gui");
+ furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&gps_uart_mutex);
+ furi_mutex_free(gps_uart->mutex);
gps_uart_disable(gps_uart);
return 0;
diff --git a/applications/plugins/gps_nmea_uart/gps_uart.h b/applications/plugins/gps_nmea_uart/gps_uart.h
index d6aafae9f..fd33eb245 100644
--- a/applications/plugins/gps_nmea_uart/gps_uart.h
+++ b/applications/plugins/gps_nmea_uart/gps_uart.h
@@ -22,6 +22,7 @@ typedef struct {
} GpsStatus;
typedef struct {
+ FuriMutex* mutex;
FuriThread* thread;
FuriStreamBuffer* rx_stream;
uint8_t rx_buf[RX_BUF_SIZE];
diff --git a/applications/plugins/hc_sr04/README.md b/applications/plugins/hc_sr04/README.md
deleted file mode 100644
index 2fb3b8ce4..000000000
--- a/applications/plugins/hc_sr04/README.md
+++ /dev/null
@@ -1 +0,0 @@
-## (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
\ No newline at end of file
diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam
index bfbb2daa8..85b6da2df 100644
--- a/applications/plugins/hc_sr04/application.fam
+++ b/applications/plugins/hc_sr04/application.fam
@@ -1,5 +1,5 @@
App(
- appid="HC_SR04_Dist_Sensor",
+ appid="hc_sr04",
name="[HC-SR] Dist. Sensor",
apptype=FlipperAppType.EXTERNAL,
entry_point="hc_sr04_app",
diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/plugins/hc_sr04/hc_sr04.c
index dbbf4f3ec..66ffdad30 100644
--- a/applications/plugins/hc_sr04/hc_sr04.c
+++ b/applications/plugins/hc_sr04/hc_sr04.c
@@ -3,6 +3,7 @@
// Ported and modified by @xMasterX
#include
+#include
#include
#include
#include
@@ -23,10 +24,11 @@ typedef struct {
} PluginEvent;
typedef struct {
+ FuriMutex* mutex;
NotificationApp* notification;
bool have_5v;
bool measurement_made;
- uint32_t echo; // ms
+ uint32_t echo; // us
float distance; // meters
} PluginState;
@@ -40,10 +42,10 @@ const NotificationSequence sequence_done = {
};
static void render_callback(Canvas* const canvas, void* ctx) {
- const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
+
// border around the edge of the screen
// canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -72,7 +74,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
FuriString* str_buf;
str_buf = furi_string_alloc();
- furi_string_printf(str_buf, "Echo: %ld ms", plugin_state->echo);
+ furi_string_printf(str_buf, "Echo: %ld us", plugin_state->echo);
canvas_draw_str_aligned(
canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf));
@@ -84,7 +86,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
}
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -110,9 +112,11 @@ static void hc_sr04_state_init(PluginState* const plugin_state) {
}
}
-float hc_sr04_ms_to_m(uint32_t ms) {
- const float speed_sound_m_per_s = 343.0f;
- const float time_s = ms / 1e3f;
+float hc_sr04_us_to_m(uint32_t us) {
+ //speed of sound for 20°C, 50% relative humidity
+ //331.3 + 20 * 0.606 + 50 * 0.0124 = 0.034404
+ const float speed_sound_m_per_s = 344.04f;
+ const float time_s = us / 1e6f;
const float total_dist = time_s * speed_sound_m_per_s;
return total_dist / 2.0f;
}
@@ -147,10 +151,6 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
furi_delay_ms(10);
furi_hal_gpio_write(&gpio_usart_tx, false);
- // TODO change from furi_get_tick(), which returns ms,
- // to DWT->CYCCNT, which is a more precise counter with
- // us precision (see furi_hal_cortex_delay_us)
-
const uint32_t start = furi_get_tick();
while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx))
@@ -158,16 +158,17 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx))
;
- const uint32_t pulse_start = furi_get_tick();
+ const uint32_t pulse_start = DWT->CYCCNT;
while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx))
;
+ const uint32_t pulse_end = DWT->CYCCNT;
- const uint32_t pulse_end = furi_get_tick();
//FURI_CRITICAL_EXIT();
- plugin_state->echo = pulse_end - pulse_start;
- plugin_state->distance = hc_sr04_ms_to_m(pulse_end - pulse_start);
+ plugin_state->echo =
+ (pulse_end - pulse_start) / furi_hal_cortex_instructions_per_microsecond();
+ plugin_state->distance = hc_sr04_us_to_m(plugin_state->echo);
plugin_state->measurement_made = true;
//furi_hal_light_set(LightRed, 0x00);
@@ -184,8 +185,8 @@ int32_t hc_sr04_app() {
furi_hal_console_disable();
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("hc_sr04", "cannot create mutex\r\n");
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
@@ -201,7 +202,7 @@ int32_t hc_sr04_app() {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -212,7 +213,7 @@ int32_t hc_sr04_app() {
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -238,7 +239,7 @@ int32_t hc_sr04_app() {
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
if(furi_hal_power_is_otg_enabled()) {
@@ -267,7 +268,8 @@ int32_t hc_sr04_app() {
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(plugin_state->mutex);
+ free(plugin_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/heap_defence_game/application.fam b/applications/plugins/heap_defence_game/application.fam
index bedd27dbc..b132531d1 100644
--- a/applications/plugins/heap_defence_game/application.fam
+++ b/applications/plugins/heap_defence_game/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Heap_Defence",
+ appid="heap_defence",
name="Heap Defence",
apptype=FlipperAppType.EXTERNAL,
entry_point="heap_defence_app",
diff --git a/applications/plugins/heap_defence_game/heap_defence.c b/applications/plugins/heap_defence_game/heap_defence.c
index a22a8db22..f7d6e4ef7 100644
--- a/applications/plugins/heap_defence_game/heap_defence.c
+++ b/applications/plugins/heap_defence_game/heap_defence.c
@@ -7,7 +7,7 @@
#include
#include "hede_assets.h"
-#include "Heap_Defence_icons.h"
+#include "heap_defence_icons.h"
#include
#include
@@ -86,6 +86,7 @@ typedef struct {
Person* person;
Animations animation;
GameStatuses game_status;
+ FuriMutex* mutex;
} GameState;
typedef Box** Field;
@@ -432,15 +433,15 @@ static void draw_box(Canvas* canvas, Box* box, int x, int y) {
static void heap_defense_render_callback(Canvas* const canvas, void* mutex) {
furi_assert(mutex);
-
- const GameState* game = acquire_mutex((ValueMutex*)mutex, 25);
+ const GameState* game = mutex;
+ furi_mutex_acquire(game->mutex, FuriWaitForever);
///Draw GameOver or Pause
if(!(game->game_status & GameStatusInProgress)) {
FURI_LOG_W(TAG, "[DAED_DRAW]func: [%s] line: %d ", __FUNCTION__, __LINE__);
canvas_draw_icon_animation(canvas, 0, 0, animations[game->animation]);
- release_mutex((ValueMutex*)mutex, game);
+ furi_mutex_release(game->mutex);
return;
}
@@ -480,7 +481,7 @@ static void heap_defense_render_callback(Canvas* const canvas, void* mutex) {
}
}
- release_mutex((ValueMutex*)mutex, game);
+ furi_mutex_release(game->mutex);
}
static void heap_defense_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -505,25 +506,25 @@ int32_t heap_defence_app(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
GameState* game = allocGameState();
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, game, sizeof(GameState))) {
+ game->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!game->mutex) {
game_destroy(game);
return 1;
}
assets_load();
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, heap_defense_render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, heap_defense_render_callback, game);
view_port_input_callback_set(view_port, heap_defense_input_callback, event_queue);
FuriTimer* timer =
furi_timer_alloc(heap_defense_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_UPDATE_FREQ);
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- NotificationApp* notification = furi_record_open("notification");
+ NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
memset(game->field[Y_LAST], 128, ROW_BYTE_SIZE);
game->person->p.y -= 2;
@@ -536,7 +537,7 @@ int32_t heap_defence_app(void* p) {
continue;
}
- game = (GameState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(game->mutex, FuriWaitForever);
//unset vibration
if(game->game_status & GameStatusVibro) {
@@ -573,7 +574,7 @@ int32_t heap_defence_app(void* p) {
notification_message(notification, &sequence_error);
}
}
- release_mutex(&state_mutex, game);
+ furi_mutex_release(game->mutex);
view_port_update(view_port);
}
@@ -581,11 +582,11 @@ int32_t heap_defence_app(void* p) {
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
- furi_record_close("gui");
- furi_record_close("notification");
+ furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_NOTIFICATION);
furi_message_queue_free(event_queue);
assets_clear();
- delete_mutex(&state_mutex);
+ furi_mutex_free(game->mutex);
game_destroy(game);
return 0;
diff --git a/applications/plugins/hex_viewer/README.md b/applications/plugins/hex_viewer/README.md
deleted file mode 100644
index e830eef8e..000000000
--- a/applications/plugins/hex_viewer/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# flipper-zero-hex-viewer
-
-Hex Viewer application for Flipper Zero!
-
-
-
-[Link to FAP](https://nightly.link/QtRoS/flipper-zero-hex-viewer/actions/artifacts/448677581.zip) (firmware version 0.72)
diff --git a/applications/plugins/hex_viewer/application.fam b/applications/plugins/hex_viewer/application.fam
index cfddabb0d..30c31ba76 100644
--- a/applications/plugins/hex_viewer/application.fam
+++ b/applications/plugins/hex_viewer/application.fam
@@ -1,6 +1,6 @@
App(
- appid="HEX_Viewer",
- name="HEX Viewer",
+ appid="hex_viewer",
+ name="Hex Viewer",
apptype=FlipperAppType.EXTERNAL,
entry_point="hex_viewer_app",
cdefines=["APP_HEX_VIEWER"],
@@ -13,5 +13,4 @@ App(
fap_icon="icons/hex_10px.png",
fap_category="Misc",
fap_icon_assets="icons",
- fap_icon_assets_symbol="hex_viewer",
)
diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam
index 86c65c2dd..b8c13e353 100644
--- a/applications/plugins/hid_app/application.fam
+++ b/applications/plugins/hid_app/application.fam
@@ -1,5 +1,5 @@
App(
- appid="USB_Remote",
+ appid="hid_usb",
name="USB Remote",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_usb_app",
@@ -10,8 +10,9 @@ App(
fap_icon_assets_symbol="hid",
)
+
App(
- appid="Bluetooth_Remote",
+ appid="hid_ble",
name="Bluetooth Remote",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_ble_app",
diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c
index 7f63f0cc6..949ff63b3 100644
--- a/applications/plugins/hid_app/hid.c
+++ b/applications/plugins/hid_app/hid.c
@@ -376,7 +376,17 @@ int32_t hid_ble_app(void* p) {
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
- bt_keys_storage_set_storage_path(app->bt, HID_BT_KEYS_STORAGE_PATH);
+ // Migrate data from old sd-card folder
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ storage_common_migrate(
+ storage,
+ EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME),
+ APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
+
+ bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
+
+ furi_record_close(RECORD_STORAGE);
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch to HID profile");
diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h
index fe32a199b..8ed1664a3 100644
--- a/applications/plugins/hid_app/hid.h
+++ b/applications/plugins/hid_app/hid.h
@@ -23,7 +23,7 @@
#include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h"
-#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
+#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
typedef enum {
HidTransportUsb,
diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/plugins/hid_app/hid_usb_10px.png
index 415de7d23..7649138eb 100644
Binary files a/applications/plugins/hid_app/hid_usb_10px.png and b/applications/plugins/hid_app/hid_usb_10px.png differ
diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c
index 82e772b15..9d29ccf5e 100644
--- a/applications/plugins/hid_app/views/hid_keyboard.c
+++ b/applications/plugins/hid_app/views/hid_keyboard.c
@@ -140,18 +140,18 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
{.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW},
},
{
- {.width = 3, .icon = NULL, .key = "Ctrl", .value = HID_KEYBOARD_L_CTRL},
+ {.width = 2, .icon = NULL, .key = "Ctl", .value = HID_KEYBOARD_L_CTRL},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL},
- {.width = 3, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT},
+ {.width = 2, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT},
- {.width = 3, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI},
+ {.width = 2, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI},
- {.width = 3, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB},
+ {.width = 2, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB},
+ {.width = 2, .icon = NULL, .key = "Esc", .value = HID_KEYBOARD_ESCAPE},
+ {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE},
+ {.width = 2, .icon = NULL, .key = "Del", .value = HID_KEYBOARD_DELETE_FORWARD},
+ {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD},
},
};
diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/plugins/hid_app/views/hid_mouse_jiggler.c
index d8f1f8928..120d5bc34 100644
--- a/applications/plugins/hid_app/views/hid_mouse_jiggler.c
+++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.c
@@ -6,6 +6,8 @@
#define TAG "HidMouseJiggler"
+#define LENGTH(x) (int)(sizeof(x) / sizeof((x)[0]))
+
struct HidMouseJiggler {
View* view;
Hid* hid;
@@ -15,10 +17,13 @@ struct HidMouseJiggler {
typedef struct {
bool connected;
bool running;
+ int interval_idx;
uint8_t counter;
HidTransport transport;
} HidMouseJigglerModel;
+const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000};
+
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseJigglerModel* model = context;
@@ -33,29 +38,39 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
}
canvas_set_font(canvas, FontPrimary);
- elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
+ elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler");
+
+ // Timeout
+ elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):");
+ canvas_set_font(canvas, FontSecondary);
+ if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7);
+ if(model->interval_idx != LENGTH(intervals) - 1)
+ canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
+ FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
+ elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
+ furi_string_free(interval_str);
canvas_set_font(canvas, FontPrimary);
- elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle");
+ elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");
canvas_set_font(canvas, FontSecondary);
// Ok
- canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
+ canvas_draw_icon(canvas, 63, 30, &I_Space_65x18);
if(model->running) {
- elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
+ elements_slightly_rounded_box(canvas, 66, 32, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
- canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
+ canvas_draw_icon(canvas, 74, 34, &I_Ok_btn_9x9);
if(model->running) {
- elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
+ elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Stop");
} else {
- elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
+ elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
- canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
- elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
+ canvas_draw_icon(canvas, 74, 54, &I_Pin_back_arrow_10x8);
+ elements_multiline_text_aligned(canvas, 91, 62, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_jiggler_timer_callback(void* context) {
@@ -76,13 +91,6 @@ static void hid_mouse_jiggler_timer_callback(void* context) {
false);
}
-static void hid_mouse_jiggler_enter_callback(void* context) {
- furi_assert(context);
- HidMouseJiggler* hid_mouse_jiggler = context;
-
- furi_timer_start(hid_mouse_jiggler->timer, 500);
-}
-
static void hid_mouse_jiggler_exit_callback(void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
@@ -95,14 +103,30 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
bool consumed = false;
- if(event->key == InputKeyOk) {
- with_view_model(
- hid_mouse_jiggler->view,
- HidMouseJigglerModel * model,
- { model->running = !model->running; },
- true);
- consumed = true;
- }
+ with_view_model(
+ hid_mouse_jiggler->view,
+ HidMouseJigglerModel * model,
+ {
+ if(event->type == InputTypePress && event->key == InputKeyOk) {
+ model->running = !model->running;
+ if(model->running) {
+ furi_timer_stop(hid_mouse_jiggler->timer);
+ furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]);
+ };
+ consumed = true;
+ }
+ if(event->type == InputTypePress && event->key == InputKeyRight && !model->running &&
+ model->interval_idx < LENGTH(intervals) - 1) {
+ model->interval_idx++;
+ consumed = true;
+ }
+ if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running &&
+ model->interval_idx > 0) {
+ model->interval_idx--;
+ consumed = true;
+ }
+ },
+ true);
return consumed;
}
@@ -116,7 +140,6 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel));
view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback);
view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback);
- view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback);
view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback);
hid_mouse_jiggler->hid = hid;
@@ -127,7 +150,10 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
- { model->transport = hid->transport; },
+ {
+ model->transport = hid->transport;
+ model->interval_idx = 2;
+ },
true);
return hid_mouse_jiggler;
diff --git a/applications/plugins/htu21d_temp_sensor/Readme.md b/applications/plugins/htu21d_temp_sensor/Readme.md
deleted file mode 100644
index 45c332306..000000000
--- a/applications/plugins/htu21d_temp_sensor/Readme.md
+++ /dev/null
@@ -1,65 +0,0 @@
-[Original link](https://github.com/Mywk/FlipperTemperatureSensor)
-
-# Flipper Temperature Sensor
-
-## Supported sensors
-
-> HTU2xD, SHT2x, SI702x, SI700x, SI701x, AM2320
-
-## What is this?
-
-A small app for the [Flipper Zero](https://flipperzero.one) that reads the [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) signal from a few temperature sensors and displays the current temperature and humidity.
-
-I'm using a [Sparkfun HTU21D sensor](https://learn.sparkfun.com/tutorials/htu21d-humidity-sensor-hookup-guide), also tested with a clone and with the Si7021 variant.
-
-
-
-
-
-
-
-# How to Connect the HTU21D sensor
-
-
-
-# How to install
-
-If you have the FAP loader, just copy the fap file from the Releases into your Flipper apps folder and you should be able to launch it from the menu.
-
-If you don't have the FAP loader you will have to bake this application together with your firmware (aka compile it all together).
-
-# FAQ
-
-## The app says the sensor is not found!
-
-1- Are the four connectors correctly soldered?
-
-2- Are the SCL and SDA connections correct? Re-check the "How to Connect the sensor" above.
-
-3- For the HTU21D, on the sensor board, there should be three contacts in the center, for it to work correctly they must be soldered together (basically drop a blob of solder to connect the three of them). Without the solder it looks like this:
-
-
-
-## Which Flipper versions was this app tested on?
-
-Version 1.2
-- RM11221439-0.71.2
-- unlshd-015
-- 0.71.1
-
-Version 1.1
-- RM10302252-0.70.1
-- unlshd-012
-- 0.68.2-1007-RM
-- 0.68.1
-- 0.67.2
-
-## I can't build the app together with the firmware?
-
-In the *application.fam*, don't forget to change the apptype, it should not be EXTERNAL but APP.
-
-# How to compile
-
-Place the temperature_sensor folder in the applications_user folder and compile using FBT.
-
-Please refer to the [Flipper Build Tool documentation](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md).
\ No newline at end of file
diff --git a/applications/plugins/htu21d_temp_sensor/application.fam b/applications/plugins/htu21d_temp_sensor/application.fam
deleted file mode 100644
index 724f55cd5..000000000
--- a/applications/plugins/htu21d_temp_sensor/application.fam
+++ /dev/null
@@ -1,14 +0,0 @@
-App(
- appid="HTU_Temperature_Sensor",
- name="[HTU/+] Temperature Sensor",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="temperature_sensor_app",
- cdefines=["APP_TEMPERATURE_SENSOR"],
- requires=[
- "gui",
- ],
- stack_size=2 * 1024,
- order=90,
- fap_icon="temperature_sensor.png",
- fap_category="GPIO",
-)
diff --git a/applications/plugins/htu21d_temp_sensor/docs/App.png b/applications/plugins/htu21d_temp_sensor/docs/App.png
deleted file mode 100644
index a0373bdbd..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/App.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/docs/Connection.png b/applications/plugins/htu21d_temp_sensor/docs/Connection.png
deleted file mode 100644
index b38f5c250..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/Connection.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png b/applications/plugins/htu21d_temp_sensor/docs/Flipper.png
deleted file mode 100644
index c85319593..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c b/applications/plugins/htu21d_temp_sensor/temperature_sensor.c
deleted file mode 100644
index b04e40a7d..000000000
--- a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/* Flipper App to read the values from a HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320 Sensor */
-/* Created by Mywk - https://github.com/Mywk - https://mywk.net */
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-
-#include
-
-#define TS_DEFAULT_VALUE 0xFFFF
-
-#define TS_AVAILABLE_SENSORS 2
-
-// HTU2XD, SHT2X, SI702X, SI700X address
-#define HTU2XD_SHT2X_SI702X_SI700X_ADDRESS (0x40 << 1)
-// SI701X ADDRESS
-#define SI701X_ADDRESS (0x41 << 1)
-
-// HTU2XD, SHT2X, SI702X, SI700X commands
-#define HTU21D_CMD_TEMPERATURE 0xE3
-#define HTU21D_CMD_HUMIDITY 0xE5
-
-// AM2320 address
-#define AM2320_ADDRESS (0x5C << 1)
-
-// Used for the temperature and humidity buffers
-#define DATA_BUFFER_SIZE 8
-
-// External I2C BUS
-#define I2C_BUS &furi_hal_i2c_handle_external
-
-// Typedef enums to make everything easier to read
-
-typedef enum { TSSCmdNone, TSSCmdTemperature, TSSCmdHumidity } TSSCmdType;
-
-typedef enum {
- TSSInitializing,
- TSSNoSensor,
- TSSPendingUpdate,
-} TSStatus;
-
-typedef enum {
- TSEventTypeTick,
- TSEventTypeInput,
-} TSEventType;
-
-typedef struct {
- TSEventType type;
- InputEvent input;
-} TSEvent;
-
-// Possible return values for sensor_cmd
-typedef enum {
- TSCmdRet_Error,
- TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X,
- TSCmdRet_SI701X,
- TSCmdRet_AM2320,
-} TSCmdRet;
-
-// External NotificationSequence RGB
-extern const NotificationSequence sequence_blink_red_100;
-extern const NotificationSequence sequence_blink_green_100;
-extern const NotificationSequence sequence_blink_blue_100;
-
-// Current status of the temperature sensor app
-static TSStatus temperature_sensor_current_status = TSSInitializing;
-
-// We keep track of the last cmd return
-static TSCmdRet temperature_sensor_last_cmd_ret = TSCmdRet_Error;
-
-// Temperature and Humidity data buffers, ready to print
-char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE];
-char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE];
-char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE];
-char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE];
-
-//
-// Executes an I2C cmd (trx)
-//
-//
-// CRC
-//
-//
-// true if fetch was successful, false otherwise
-//
-static TSCmdRet temperature_sensor_cmd(TSSCmdType cmd, uint8_t* buffer) {
- uint32_t timeout = furi_ms_to_ticks(100);
- TSCmdRet ret = TSCmdRet_Error;
-
- // Aquire I2C and check if device is ready, then release
- furi_hal_i2c_acquire(I2C_BUS);
-
- // Check if HTU2XD, SHT2X, SI702X, SI700X sensor is available
- uint8_t isAddress40 =
- furi_hal_i2c_is_device_ready(I2C_BUS, HTU2XD_SHT2X_SI702X_SI700X_ADDRESS, timeout);
- uint8_t isAddress41 = 0;
-
- // Check if SI701X sensor is available if necessary
- if(!isAddress40) isAddress41 = furi_hal_i2c_is_device_ready(I2C_BUS, SI701X_ADDRESS, timeout);
-
- if(isAddress40 || isAddress41) {
- uint8_t address = isAddress40 ? HTU2XD_SHT2X_SI702X_SI700X_ADDRESS : SI701X_ADDRESS;
-
- // Better safe than sorry delay
- furi_delay_ms(15);
-
- // Extra delay for the SI70XX
- if(isAddress41) furi_delay_ms(50);
-
- // Transmit either the temperature or the humidity command depending on TSSCmdType
- uint8_t c = (cmd == TSSCmdTemperature) ? HTU21D_CMD_TEMPERATURE : HTU21D_CMD_HUMIDITY;
- if(furi_hal_i2c_tx(I2C_BUS, address, &c, 1, timeout)) {
- // Receive data (2 bytes)
- if(furi_hal_i2c_rx(I2C_BUS, address, buffer, 2, timeout + 50))
- ret = isAddress40 ? TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X : TSCmdRet_SI701X;
- }
- } else {
- // The AM2320 goes to sleep after a period of inactivity, wake it up (check AM2320 datasheet for more info)
- furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout);
- furi_delay_ms(30);
-
- // Check if it's really available
- if(furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout)) {
- // {Address, Register, Len}
- const uint8_t request[3] = {0x03, 0x00, 0x04};
-
- if(furi_hal_i2c_tx(I2C_BUS, AM2320_ADDRESS, request, 3, timeout)) {
- // 6 bytes - usually 8 but we currently don't check the CRC
- if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, 6, timeout))
- ret = TSCmdRet_AM2320;
- }
- }
- }
-
- furi_hal_i2c_release(I2C_BUS);
-
- temperature_sensor_last_cmd_ret = ret;
- return ret;
-}
-
-//
-// Fetches temperature and humidity from sensor
-//
-//
-// temperature in C
-// humidity in relative humidity
-//
-//
-// Temperature and humidity must be preallocated
-//
-//
-// CRC
-//
-//
-// true if fetch was successful, false otherwise
-//
-static bool temperature_sensor_fetch_data(double* temperature, double* humidity) {
- bool ret = false;
-
- uint16_t adc_raw;
-
- uint8_t buffer[DATA_BUFFER_SIZE] = {0x00};
-
- // Check if the sensor is the HTU21D by attempting to fetch the temperature
- TSCmdRet cmdRet = temperature_sensor_cmd(TSSCmdTemperature, buffer);
- if(cmdRet == TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X || cmdRet == TSCmdRet_SI701X) {
- // Calculate temperature
- adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
- *temperature = (float)(adc_raw * 175.72 / 65536.00) - 46.85;
-
- // Fetch humidity
- if(temperature_sensor_cmd(TSSCmdHumidity, buffer)) {
- // Calculate humidity
- adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
- *humidity = (float)(adc_raw * 125.0 / 65536.00) - 6.0;
-
- ret = true;
- }
- } else if(cmdRet == TSCmdRet_AM2320) {
- // The AM2320 returns all the data immediately so we just process it all
- // Note: CRC isn't currently present in the buffer
-
- // Temperature
- float temp = (((buffer[4] & 0x7F) << 8) + buffer[5]) / 10;
- *temperature = ((buffer[4] & 0x80) >> 7) == 1 ? temp * (-1) : temp;
-
- // Humidity
- temp = ((buffer[2] << 8) + buffer[3]) / 10;
- *humidity = temp;
-
- ret = true;
- }
-
- return ret;
-}
-
-//
-// Draw callback
-//
-static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) {
- UNUSED(ctx);
-
- canvas_clear(canvas);
- canvas_set_font(canvas, FontPrimary);
-
- // Update title accordingly (this could be improved by checking the hardware id)
- switch(temperature_sensor_last_cmd_ret) {
- case TSCmdRet_Error:
- canvas_draw_str(canvas, 2, 10, "Temperature Sensor");
- break;
-
- case TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X:
- canvas_draw_str(canvas, 2, 10, "HTU/SHT/SI70 Sensor");
- break;
-
- case TSCmdRet_SI701X:
- canvas_draw_str(canvas, 2, 10, "SI701X Sensor");
- break;
-
- case TSCmdRet_AM2320:
- canvas_draw_str(canvas, 2, 10, "AM2320 Sensor");
- break;
-
- default:
- break;
- }
-
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 62, "Press back to exit.");
-
- switch(temperature_sensor_current_status) {
- case TSSInitializing:
- canvas_draw_str(canvas, 2, 30, "Initializing..");
- break;
- case TSSNoSensor:
- canvas_draw_str(canvas, 2, 30, "No sensor found!");
- break;
- case TSSPendingUpdate: {
- canvas_draw_str(canvas, 3, 24, "Temperature");
- canvas_draw_str(canvas, 68, 24, "Humidity");
-
- // Draw vertical lines
- canvas_draw_line(canvas, 61, 16, 61, 50);
- canvas_draw_line(canvas, 62, 16, 62, 50);
-
- // Draw horizontal line
- canvas_draw_line(canvas, 2, 27, 122, 27);
-
- // Draw temperature and humidity values
- canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c);
- canvas_draw_str(canvas, 42, 38, "C");
- canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f);
- canvas_draw_str(canvas, 42, 48, "F");
- canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity);
- canvas_draw_str(canvas, 100, 38, "%");
- canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity);
- canvas_draw_str(canvas, 100, 48, "g/m3");
- } break;
- default:
- break;
- }
-}
-
-//
-// Input callback
-//
-static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
-
- TSEvent event = {.type = TSEventTypeInput, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
-
-//
-// Timer callback
-//
-static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) {
- furi_assert(event_queue);
-
- TSEvent event = {.type = TSEventTypeTick};
- furi_message_queue_put(event_queue, &event, 0);
-}
-
-//
-// App entry point
-//
-int32_t temperature_sensor_app(void* p) {
- UNUSED(p);
-
- // Declare our variables and assign variables a default value
- TSEvent tsEvent;
- bool sensorFound = false;
- double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE;
-
- // Used for absolute humidity calculation
- double vapour_pressure = 0;
-
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent));
-
- // Register callbacks
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL);
- view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue);
-
- // Create timer and register its callback
- FuriTimer* timer =
- furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue);
- furi_timer_start(timer, furi_kernel_get_tick_frequency());
-
- // Register viewport
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
- // Used to notify the user by blinking red (error) or blue (fetch successful)
- NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
-
- while(1) {
- furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk);
-
- // Handle events
- if(tsEvent.type == TSEventTypeInput) {
- // Exit on back key
- if(tsEvent.input.key ==
- InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort)
- break;
- } else if(tsEvent.type == TSEventTypeTick) {
- // Update sensor data
- // Fetch data and set the sensor current status accordingly
- sensorFound = temperature_sensor_fetch_data(&celsius, &rel_humidity);
- temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor);
-
- if(sensorFound) {
- // Blink blue
- notification_message(notifications, &sequence_blink_blue_100);
-
- if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) {
- // Convert celsius to fahrenheit
- fahrenheit = (celsius * 9 / 5) + 32;
-
- // Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1
- // Calculate saturation vapour pressure first
- vapour_pressure =
- (double)6.11 *
- pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius)));
- // Then the vapour pressure in Pa
- vapour_pressure = vapour_pressure * rel_humidity;
- // Calculate absolute humidity
- abs_humidity =
- (double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius));
-
- // Fill our buffers here, not on the canvas draw callback
- snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius);
- snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit);
- snprintf(
- ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity);
- snprintf(
- ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity);
- }
- } else {
- // Reset our variables to their default values
- celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE;
-
- // Blink red
- notification_message(notifications, &sequence_blink_red_100);
- }
- }
-
- furi_delay_ms(!sensorFound ? 100 : 500);
- }
-
- // Dobby is freee (free our variables, Flipper will crash if we don't do this!)
- furi_timer_free(timer);
- gui_remove_view_port(gui, view_port);
- view_port_free(view_port);
- furi_message_queue_free(event_queue);
-
- furi_record_close(RECORD_NOTIFICATION);
- furi_record_close(RECORD_GUI);
-
- return 0;
-}
\ No newline at end of file
diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png b/applications/plugins/htu21d_temp_sensor/temperature_sensor.png
deleted file mode 100644
index b6fe6d7fe..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png and /dev/null differ
diff --git a/applications/plugins/ibtn_fuzzer/README.md b/applications/plugins/ibtn_fuzzer/README.md
deleted file mode 100644
index 6b93488c8..000000000
--- a/applications/plugins/ibtn_fuzzer/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# iButton Fuzzer
-Plugin for Flipper Zero
-
-Made for [Unleashed](https://github.com/DarkFlippers/unleashed-firmware) firmware, but will work on original firmware too
-
-Supported protocols:
-- DS1990
-- Metakom
-- Cyfral
\ No newline at end of file
diff --git a/applications/plugins/ibtn_fuzzer/application.fam b/applications/plugins/ibtn_fuzzer/application.fam
index 93a9bad23..b27f47ba9 100644
--- a/applications/plugins/ibtn_fuzzer/application.fam
+++ b/applications/plugins/ibtn_fuzzer/application.fam
@@ -1,5 +1,5 @@
App(
- appid="IBtn_Fuzzer",
+ appid="iBtn_Fuzzer",
name="iButton Fuzzer",
apptype=FlipperAppType.EXTERNAL,
entry_point="ibtnfuzzer_start",
diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c
index f02da3b82..c5f2a5f7c 100644
--- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c
+++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c
@@ -9,11 +9,9 @@
#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer"
static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) {
- iBtnFuzzerState* ibtnfuzzer_state = (iBtnFuzzerState*)acquire_mutex((ValueMutex*)ctx, 100);
-
- if(ibtnfuzzer_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ iBtnFuzzerState* ibtnfuzzer_state = ctx;
+ furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever);
// Draw correct Canvas
switch(ibtnfuzzer_state->current_scene) {
@@ -35,7 +33,7 @@ static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) {
break;
}
- release_mutex((ValueMutex*)ctx, ibtnfuzzer_state);
+ furi_mutex_release(ibtnfuzzer_state->mutex);
}
void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -120,11 +118,9 @@ int32_t ibtnfuzzer_start(void* p) {
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent));
iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc();
- ValueMutex ibtnfuzzer_state_mutex;
- // Mutex
- FURI_LOG_I(TAG, "Initializing ibtnfuzzer mutex");
- if(!init_mutex(&ibtnfuzzer_state_mutex, ibtnfuzzer_state, sizeof(iBtnFuzzerState))) {
+ ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!ibtnfuzzer_state->mutex) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
@@ -141,7 +137,7 @@ int32_t ibtnfuzzer_start(void* p) {
// Configure view port
FURI_LOG_I(TAG, "Initializing viewport");
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, &ibtnfuzzer_state_mutex);
+ view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, ibtnfuzzer_state);
view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue);
// Configure timer
@@ -160,6 +156,7 @@ int32_t ibtnfuzzer_start(void* p) {
while(ibtnfuzzer_state->is_running) {
// Get next event
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
+ //furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
if(event.evt_type == EventTypeKey) {
//Handle event key
@@ -250,6 +247,7 @@ int32_t ibtnfuzzer_start(void* p) {
view_port_update(view_port);
}
}
+ //furi_mutex_release(ibtnfuzzer_state->mutex);
}
// Cleanup
@@ -262,6 +260,7 @@ int32_t ibtnfuzzer_start(void* p) {
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
+ furi_mutex_free(ibtnfuzzer_state->mutex);
ibtnfuzzer_free(ibtnfuzzer_state);
return 0;
diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
index 1db527f65..91a9c6b0c 100644
--- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
+++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
@@ -15,7 +15,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -56,6 +56,7 @@ typedef struct {
// STRUCTS
typedef struct {
+ FuriMutex* mutex;
bool is_running;
bool is_attacking;
iBtnFuzzerScene current_scene;
@@ -78,7 +79,8 @@ typedef struct {
uint8_t key_index;
iButtonWorker* worker;
iButtonKey* key;
- iButtonKeyType keytype;
+ iButtonProtocolId keytype;
+ iButtonProtocols* protocols;
bool workr_rund;
bool enter_rerun;
bool attack_stop_called;
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
index e45035d6b..1cab8b04e 100644
--- a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
+++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
@@ -72,14 +72,15 @@ void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) {
context->time_between_cards = 8;
context->attack_step = 0;
context->attack_stop_called = false;
- context->key = ibutton_key_alloc();
- context->worker = ibutton_worker_alloc();
+ context->protocols = ibutton_protocols_alloc();
+ context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols));
+ context->worker = ibutton_worker_alloc(context->protocols);
if(context->proto == Metakom) {
- context->keytype = iButtonKeyMetakom;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom");
} else if(context->proto == Cyfral) {
- context->keytype = iButtonKeyCyfral;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral");
} else {
- context->keytype = iButtonKeyDS1990;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990");
}
context->workr_rund = false;
}
@@ -90,8 +91,9 @@ void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) {
ibutton_worker_stop_thread(context->worker);
context->workr_rund = false;
}
- ibutton_worker_free(context->worker);
ibutton_key_free(context->key);
+ ibutton_worker_free(context->worker);
+ ibutton_protocols_free(context->protocols);
notification_message(context->notify, &sequence_blink_stop);
}
@@ -99,9 +101,14 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
if(context->is_attacking) {
if(1 == counter) {
ibutton_worker_start_thread(context->worker);
- ibutton_key_set_type(context->key, context->keytype);
- ibutton_key_set_data(
- context->key, context->payload, ibutton_key_get_size_by_type(context->keytype));
+ ibutton_key_set_protocol_id(context->key, context->keytype);
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(context->protocols, context->key, &data);
+ data.size = sizeof(context->payload);
+ for(size_t i = 0; i < data.size; i++) {
+ data.ptr[i] = context->payload[i];
+ }
+
ibutton_worker_emulate_start(context->worker, context->key);
context->workr_rund = true;
} else if(0 == counter) {
diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam
index 75e5beb67..6a3c4986e 100644
--- a/applications/plugins/ifttt/application.fam
+++ b/applications/plugins/ifttt/application.fam
@@ -10,5 +10,5 @@ App(
stack_size=2 * 1024,
order=20,
fap_icon="icon.png",
- fap_category="GPIO",
+ fap_category="WiFi",
)
diff --git a/applications/plugins/lightmeter/.clang-format b/applications/plugins/lightmeter/.clang-format
deleted file mode 100644
index 4b76f7fa4..000000000
--- a/applications/plugins/lightmeter/.clang-format
+++ /dev/null
@@ -1,191 +0,0 @@
----
-Language: Cpp
-AccessModifierOffset: -4
-AlignAfterOpenBracket: AlwaysBreak
-AlignArrayOfStructures: None
-AlignConsecutiveMacros: None
-AlignConsecutiveAssignments: None
-AlignConsecutiveBitFields: None
-AlignConsecutiveDeclarations: None
-AlignEscapedNewlines: Left
-AlignOperands: Align
-AlignTrailingComments: false
-AllowAllArgumentsOnNextLine: true
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortEnumsOnASingleLine: true
-AllowShortBlocksOnASingleLine: Never
-AllowShortCaseLabelsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: None
-AllowShortLambdasOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: WithoutElse
-AllowShortLoopsOnASingleLine: true
-AlwaysBreakAfterDefinitionReturnType: None
-AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: Yes
-AttributeMacros:
- - __capability
-BinPackArguments: false
-BinPackParameters: false
-BraceWrapping:
- AfterCaseLabel: false
- AfterClass: false
- AfterControlStatement: Never
- AfterEnum: false
- AfterFunction: false
- AfterNamespace: false
- AfterObjCDeclaration: false
- AfterStruct: false
- AfterUnion: false
- AfterExternBlock: false
- BeforeCatch: false
- BeforeElse: false
- BeforeLambdaBody: false
- BeforeWhile: false
- IndentBraces: false
- SplitEmptyFunction: true
- SplitEmptyRecord: true
- SplitEmptyNamespace: true
-BreakBeforeBinaryOperators: None
-BreakBeforeConceptDeclarations: true
-BreakBeforeBraces: Attach
-BreakBeforeInheritanceComma: false
-BreakInheritanceList: BeforeColon
-BreakBeforeTernaryOperators: false
-BreakConstructorInitializersBeforeComma: false
-BreakConstructorInitializers: BeforeComma
-BreakAfterJavaFieldAnnotations: false
-BreakStringLiterals: false
-ColumnLimit: 99
-CommentPragmas: '^ IWYU pragma:'
-QualifierAlignment: Leave
-CompactNamespaces: false
-ConstructorInitializerIndentWidth: 4
-ContinuationIndentWidth: 4
-Cpp11BracedListStyle: true
-DeriveLineEnding: true
-DerivePointerAlignment: false
-DisableFormat: false
-EmptyLineAfterAccessModifier: Never
-EmptyLineBeforeAccessModifier: LogicalBlock
-ExperimentalAutoDetectBinPacking: false
-PackConstructorInitializers: BinPack
-BasedOnStyle: ''
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-AllowAllConstructorInitializersOnNextLine: true
-FixNamespaceComments: false
-ForEachMacros:
- - foreach
- - Q_FOREACH
- - BOOST_FOREACH
-IfMacros:
- - KJ_IF_MAYBE
-IncludeBlocks: Preserve
-IncludeCategories:
- - Regex: '.*'
- Priority: 1
- SortPriority: 0
- CaseSensitive: false
- - Regex: '^(<|"(gtest|gmock|isl|json)/)'
- Priority: 3
- SortPriority: 0
- CaseSensitive: false
- - Regex: '.*'
- Priority: 1
- SortPriority: 0
- CaseSensitive: false
-IncludeIsMainRegex: '(Test)?$'
-IncludeIsMainSourceRegex: ''
-IndentAccessModifiers: false
-IndentCaseLabels: false
-IndentCaseBlocks: false
-IndentGotoLabels: true
-IndentPPDirectives: None
-IndentExternBlock: AfterExternBlock
-IndentRequires: false
-IndentWidth: 4
-IndentWrappedFunctionNames: true
-InsertTrailingCommas: None
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
-KeepEmptyLinesAtTheStartOfBlocks: false
-LambdaBodyIndentation: Signature
-MacroBlockBegin: ''
-MacroBlockEnd: ''
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
-ObjCBinPackProtocolList: Auto
-ObjCBlockIndentWidth: 4
-ObjCBreakBeforeNestedBlockParam: true
-ObjCSpaceAfterProperty: true
-ObjCSpaceBeforeProtocolList: true
-PenaltyBreakAssignment: 10
-PenaltyBreakBeforeFirstCallParameter: 30
-PenaltyBreakComment: 10
-PenaltyBreakFirstLessLess: 0
-PenaltyBreakOpenParenthesis: 0
-PenaltyBreakString: 10
-PenaltyBreakTemplateDeclaration: 10
-PenaltyExcessCharacter: 100
-PenaltyReturnTypeOnItsOwnLine: 60
-PenaltyIndentedWhitespace: 0
-PointerAlignment: Left
-PPIndentWidth: -1
-ReferenceAlignment: Pointer
-ReflowComments: false
-RemoveBracesLLVM: false
-SeparateDefinitionBlocks: Leave
-ShortNamespaceLines: 1
-SortIncludes: Never
-SortJavaStaticImport: Before
-SortUsingDeclarations: false
-SpaceAfterCStyleCast: false
-SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: true
-SpaceBeforeAssignmentOperators: true
-SpaceBeforeCaseColon: false
-SpaceBeforeCpp11BracedList: false
-SpaceBeforeCtorInitializerColon: true
-SpaceBeforeInheritanceColon: true
-SpaceBeforeParens: Never
-SpaceBeforeParensOptions:
- AfterControlStatements: false
- AfterForeachMacros: false
- AfterFunctionDefinitionName: false
- AfterFunctionDeclarationName: false
- AfterIfMacros: false
- AfterOverloadedOperator: false
- BeforeNonEmptyParentheses: false
-SpaceAroundPointerQualifiers: Default
-SpaceBeforeRangeBasedForLoopColon: true
-SpaceInEmptyBlock: false
-SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 1
-SpacesInAngles: Never
-SpacesInConditionalStatement: false
-SpacesInContainerLiterals: false
-SpacesInCStyleCastParentheses: false
-SpacesInLineCommentPrefix:
- Minimum: 1
- Maximum: -1
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-SpaceBeforeSquareBrackets: false
-BitFieldColonSpacing: Both
-Standard: c++03
-StatementAttributeLikeMacros:
- - Q_EMIT
-StatementMacros:
- - Q_UNUSED
- - QT_REQUIRE_VERSION
-TabWidth: 4
-UseCRLF: false
-UseTab: Never
-WhitespaceSensitiveMacros:
- - STRINGIZE
- - PP_STRINGIZE
- - BOOST_PP_STRINGIZE
- - NS_SWIFT_NAME
- - CF_SWIFT_NAME
-...
-
diff --git a/applications/plugins/lightmeter/README.md b/applications/plugins/lightmeter/README.md
index dc6c6ffd5..d9c071e67 100644
--- a/applications/plugins/lightmeter/README.md
+++ b/applications/plugins/lightmeter/README.md
@@ -1,10 +1,9 @@
-# flipperzero-lightmeter
+# flipperzero-lightmeter
-[](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml)
+[Original link](https://github.com/oleksiikutuzov/flipperzero-lightmeter)
-
-
+
## Wiring
@@ -15,19 +14,6 @@ SCL -> C0
SDA -> C1
```
-## Sensor module
-
-
-
-### If you want to build this module, you'll need (it's quite over-engineered, sorry :D)
-1. [Module PCB](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_gerber.zip)
-2. [Enclosure](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_enclosure.stl)
-3. 4-pin female header
-4. 10-pin male header
-5. 2x M3 threaded inserts (max diameter 5.3 mm, max height 4 mm)
-6. 2x M3x5 screws
-
-
## TODO
- [ ] Save settings to sd card
diff --git a/applications/plugins/lightmeter/application.fam b/applications/plugins/lightmeter/application.fam
index b1de62641..8cd90ee26 100644
--- a/applications/plugins/lightmeter/application.fam
+++ b/applications/plugins/lightmeter/application.fam
@@ -1,5 +1,5 @@
App(
- appid="BH1750_Lightmeter",
+ appid="lightmeter",
name="[BH1750] Lightmeter",
apptype=FlipperAppType.EXTERNAL,
entry_point="lightmeter_app",
@@ -9,7 +9,6 @@ App(
],
stack_size=1 * 1024,
order=90,
- fap_version=(0, 7),
fap_icon="lightmeter.png",
fap_category="GPIO",
fap_private_libs=[
@@ -22,7 +21,4 @@ App(
),
],
fap_icon_assets="icons",
- fap_description="Lightmeter app for photography based on BH1750 sensor",
- fap_author="Oleksii Kutuzov",
- fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter",
)
diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/plugins/lightmeter/gui/views/main_view.c
index fcbafbff4..8b3e2989f 100644
--- a/applications/plugins/lightmeter/gui/views/main_view.c
+++ b/applications/plugins/lightmeter/gui/views/main_view.c
@@ -62,11 +62,11 @@ const float aperture_numbers[] = {
const float speed_numbers[] = {
[SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000,
[SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250,
- [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_30] = 1.0 / 30,
- [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, [SPEED_4] = 1.0 / 4,
- [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, [SPEED_2S] = 2.0,
- [SPEED_4S] = 4.0, [SPEED_8S] = 8.0, [SPEED_15S] = 15.0,
- [SPEED_30S] = 30.0,
+ [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_48] = 1.0 / 48,
+ [SPEED_30] = 1.0 / 30, [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8,
+ [SPEED_4] = 1.0 / 4, [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0,
+ [SPEED_2S] = 2.0, [SPEED_4S] = 4.0, [SPEED_8S] = 8.0,
+ [SPEED_15S] = 15.0, [SPEED_30S] = 30.0,
};
struct MainView {
diff --git a/applications/plugins/lightmeter/gui/views/main_view.h b/applications/plugins/lightmeter/gui/views/main_view.h
index 42d2d1877..038cd3065 100644
--- a/applications/plugins/lightmeter/gui/views/main_view.h
+++ b/applications/plugins/lightmeter/gui/views/main_view.h
@@ -1,7 +1,7 @@
#pragma once
#include
-#include "BH1750_Lightmeter_icons.h"
+#include "lightmeter_icons.h"
#include "../../lightmeter_config.h"
typedef struct MainView MainView;
diff --git a/applications/plugins/lightmeter/images/framed_gui_config.png b/applications/plugins/lightmeter/images/framed_gui_config.png
deleted file mode 100644
index b87c3bd5c..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_config.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png b/applications/plugins/lightmeter/images/framed_gui_lux_meter.png
deleted file mode 100644
index 6ab0cf191..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/framed_gui_main.png b/applications/plugins/lightmeter/images/framed_gui_main.png
deleted file mode 100644
index 23dc4a2cc..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_main.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_config.png b/applications/plugins/lightmeter/images/gui_config.png
deleted file mode 100644
index 95fdcb167..000000000
Binary files a/applications/plugins/lightmeter/images/gui_config.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_lux_meter.png b/applications/plugins/lightmeter/images/gui_lux_meter.png
deleted file mode 100644
index f7159e671..000000000
Binary files a/applications/plugins/lightmeter/images/gui_lux_meter.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_main.png b/applications/plugins/lightmeter/images/gui_main.png
deleted file mode 100644
index ba4c3f75f..000000000
Binary files a/applications/plugins/lightmeter/images/gui_main.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/plugins/lightmeter/lib/BH1750/BH1750.c
index 28616e040..9e6a50758 100644
--- a/applications/plugins/lightmeter/lib/BH1750/BH1750.c
+++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.c
@@ -15,6 +15,7 @@
BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode
uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value
+uint8_t bh1750_addr = BH1750_ADDRESS;
BH1750_STATUS bh1750_init() {
if(BH1750_OK == bh1750_reset()) {
@@ -25,12 +26,17 @@ BH1750_STATUS bh1750_init() {
return BH1750_ERROR;
}
+BH1750_STATUS bh1750_init_with_addr(uint8_t addr) {
+ bh1750_addr = (addr << 1);
+ return bh1750_init();
+}
+
BH1750_STATUS bh1750_reset() {
uint8_t command = 0x07;
bool status;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &command, 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &command, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -45,7 +51,7 @@ BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) {
bool status;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &PowerOn, 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &PowerOn, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -69,7 +75,7 @@ BH1750_STATUS bh1750_set_mode(BH1750_mode mode) {
bh1750_mode = mode;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &mode, 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &mode, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -93,14 +99,14 @@ BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) {
tmp[1] = (0x60 | (mt_reg & 0x1F));
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[0], 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[0], 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(!status) {
return BH1750_ERROR;
}
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[1], 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[1], 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
return BH1750_OK;
@@ -122,7 +128,7 @@ BH1750_STATUS bh1750_read_light(float* result) {
bool status;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_rx(I2C_BUS, BH1750_ADDRESS, rcv, 2, I2C_TIMEOUT);
+ status = furi_hal_i2c_rx(I2C_BUS, bh1750_addr, rcv, 2, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/plugins/lightmeter/lib/BH1750/BH1750.h
index 991350f7f..46649c33e 100644
--- a/applications/plugins/lightmeter/lib/BH1750/BH1750.h
+++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.h
@@ -49,6 +49,13 @@ typedef enum {
*/
BH1750_STATUS bh1750_init();
+/**
+ * @brief Change the I2C device address and then initialize the sensor.
+ *
+ * @return BH1750_STATUS
+ */
+BH1750_STATUS bh1750_init_with_addr(uint8_t addr);
+
/**
* @brief Reset all registers to the default value.
*
diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/plugins/lightmeter/lightmeter_config.h
index 4f7e11e0d..5edbdce0a 100644
--- a/applications/plugins/lightmeter/lightmeter_config.h
+++ b/applications/plugins/lightmeter/lightmeter_config.h
@@ -79,6 +79,7 @@ typedef enum {
SPEED_250,
SPEED_125,
SPEED_60,
+ SPEED_48,
SPEED_30,
SPEED_15,
SPEED_8,
diff --git a/applications/plugins/lightmeter/module/back.jpg b/applications/plugins/lightmeter/module/back.jpg
deleted file mode 100644
index 366d7a852..000000000
Binary files a/applications/plugins/lightmeter/module/back.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/front.jpg b/applications/plugins/lightmeter/module/front.jpg
deleted file mode 100644
index b38fd4150..000000000
Binary files a/applications/plugins/lightmeter/module/front.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module.jpg b/applications/plugins/lightmeter/module/module.jpg
deleted file mode 100644
index 53f481338..000000000
Binary files a/applications/plugins/lightmeter/module/module.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module_v2_enclosure.stl b/applications/plugins/lightmeter/module/module_v2_enclosure.stl
deleted file mode 100644
index ee2be93fd..000000000
Binary files a/applications/plugins/lightmeter/module/module_v2_enclosure.stl and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module_v2_gerber.zip b/applications/plugins/lightmeter/module/module_v2_gerber.zip
deleted file mode 100644
index 02887f4bf..000000000
Binary files a/applications/plugins/lightmeter/module/module_v2_gerber.zip and /dev/null differ
diff --git a/applications/plugins/mandelbrot/mandelbrot.c b/applications/plugins/mandelbrot/mandelbrot.c
index bfddc6a97..8bfe36c77 100644
--- a/applications/plugins/mandelbrot/mandelbrot.c
+++ b/applications/plugins/mandelbrot/mandelbrot.c
@@ -15,6 +15,7 @@ typedef struct {
} PluginEvent;
typedef struct {
+ FuriMutex* mutex;
float xZoom;
float yZoom;
float xOffset;
@@ -52,10 +53,9 @@ bool mandelbrot_pixel(int x, int y, float xZoom, float yZoom, float xOffset, flo
}
static void render_callback(Canvas* const canvas, void* ctx) {
- const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
// border around the edge of the screen
canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -75,7 +75,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
}
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -100,8 +100,8 @@ int32_t mandelbrot_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
mandelbrot_state_init(plugin_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("mandelbrot", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
@@ -110,7 +110,7 @@ int32_t mandelbrot_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -120,7 +120,7 @@ int32_t mandelbrot_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -154,12 +154,9 @@ int32_t mandelbrot_app(void* p) {
}
}
}
- } else {
- FURI_LOG_D("mandelbrot", "osMessageQueue: event timeout");
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
view_port_enabled_set(view_port, false);
@@ -167,6 +164,8 @@ int32_t mandelbrot_app(void* p) {
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
+ furi_mutex_free(plugin_state->mutex);
+ free(plugin_state);
return 0;
}
\ No newline at end of file
diff --git a/applications/plugins/metronome/README.md b/applications/plugins/metronome/README.md
index 79134c350..4b6cd3122 100644
--- a/applications/plugins/metronome/README.md
+++ b/applications/plugins/metronome/README.md
@@ -1,5 +1,7 @@
# Metronome
+[Original link](https://github.com/panki27/Metronome)
+
A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along perfectly with my [BPM tapper](https://github.com/panki27/bpm-tapper).

@@ -17,5 +19,5 @@ A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along
## Compiling
```
-./fbt fap_metronome
+./fbt firmware_metronome
```
diff --git a/applications/plugins/metronome/application.fam b/applications/plugins/metronome/application.fam
index b40809638..32588d06e 100644
--- a/applications/plugins/metronome/application.fam
+++ b/applications/plugins/metronome/application.fam
@@ -8,8 +8,8 @@ App(
"gui",
],
fap_icon="metronome_icon.png",
- fap_icon_assets="icons",
fap_category="Music",
+ fap_icon_assets="images",
stack_size=2 * 1024,
order=20,
)
diff --git a/applications/plugins/metronome/gui_extensions.c b/applications/plugins/metronome/gui_extensions.c
index a7661ec28..458eb137b 100644
--- a/applications/plugins/metronome/gui_extensions.c
+++ b/applications/plugins/metronome/gui_extensions.c
@@ -1,6 +1,6 @@
#include
#include
-#include "Metronome_icons.h"
+#include
//lib can only do bottom left/right
void elements_button_top_left(Canvas* canvas, const char* str) {
diff --git a/applications/plugins/metronome/icons/ButtonUp_7x4.png b/applications/plugins/metronome/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/metronome/icons/ButtonUp_7x4.png
rename to applications/plugins/metronome/images/ButtonUp_7x4.png
diff --git a/applications/plugins/metronome/metronome.c b/applications/plugins/metronome/metronome.c
index 4c374aa75..a01f4418d 100644
--- a/applications/plugins/metronome/metronome.c
+++ b/applications/plugins/metronome/metronome.c
@@ -1,7 +1,6 @@
#include
#include
#include
-#include
#include
#include
@@ -50,31 +49,33 @@ typedef struct {
enum OutputMode output_mode;
FuriTimer* timer;
NotificationApp* notifications;
+ FuriMutex* mutex;
} MetronomeState;
static void render_callback(Canvas* const canvas, void* ctx) {
- const MetronomeState* metronome_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(metronome_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const MetronomeState* metronome_state = ctx;
+ furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
- string_t tempStr;
- string_init(tempStr);
+ FuriString* tempStr = furi_string_alloc();
canvas_draw_frame(canvas, 0, 0, 128, 64);
canvas_set_font(canvas, FontPrimary);
// draw bars/beat
- string_printf(tempStr, "%d/%d", metronome_state->beats_per_bar, metronome_state->note_length);
- canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr));
- string_reset(tempStr);
+ furi_string_printf(
+ tempStr, "%d/%d", metronome_state->beats_per_bar, metronome_state->note_length);
+ canvas_draw_str_aligned(
+ canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
// draw BPM value
- string_printf(tempStr, "%.2f", metronome_state->bpm);
+ furi_string_printf(tempStr, "%.2f", metronome_state->bpm);
canvas_set_font(canvas, FontBigNumbers);
- canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr));
- string_reset(tempStr);
+ canvas_draw_str_aligned(
+ canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
+ furi_string_reset(tempStr);
// draw volume indicator
// always draw first waves
@@ -126,8 +127,8 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas, 8, 36, 112, (float)metronome_state->current_beat / metronome_state->beats_per_bar);
// cleanup
- string_clear(tempStr);
- release_mutex((ValueMutex*)ctx, metronome_state);
+ furi_string_free(tempStr);
+ furi_mutex_release(metronome_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -139,7 +140,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
static void timer_callback(void* ctx) {
// this is where we go BEEP!
- MetronomeState* metronome_state = acquire_mutex((ValueMutex*)ctx, 25);
+ furi_assert(ctx);
+ MetronomeState* metronome_state = ctx;
+ furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
+
metronome_state->current_beat++;
if(metronome_state->current_beat > metronome_state->beats_per_bar) {
metronome_state->current_beat = 1;
@@ -200,7 +204,7 @@ static void timer_callback(void* ctx) {
}
notification_message(metronome_state->notifications, &sequence_reset_rgb);
- release_mutex((ValueMutex*)ctx, metronome_state);
+ furi_mutex_release(metronome_state->mutex);
}
static uint32_t state_to_sleep_ticks(MetronomeState* metronome_state) {
@@ -273,8 +277,8 @@ int32_t metronome_app() {
MetronomeState* metronome_state = malloc(sizeof(MetronomeState));
metronome_state_init(metronome_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, metronome_state, sizeof(MetronomeState))) {
+ metronome_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!metronome_state->mutex) {
FURI_LOG_E("Metronome", "cannot create mutex\r\n");
free(metronome_state);
return 255;
@@ -282,20 +286,20 @@ int32_t metronome_app() {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, metronome_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
- metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex);
+ metronome_state->timer =
+ furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, metronome_state);
// Open GUI and register view_port
- //
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- MetronomeState* metronome_state = (MetronomeState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
@@ -326,7 +330,7 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
} else if(event.input.type == InputTypeLong) {
@@ -348,7 +352,7 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
} else if(event.input.type == InputTypeRepeat) {
@@ -369,26 +373,23 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
}
}
- } else {
- FURI_LOG_D("Metronome", "FuriMessageQueue: event timeout");
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, metronome_state);
+ furi_mutex_release(metronome_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
- furi_record_close("gui");
+ furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(metronome_state->mutex);
furi_timer_free(metronome_state->timer);
furi_record_close(RECORD_NOTIFICATION);
free(metronome_state);
diff --git a/applications/plugins/minesweeper/README.md b/applications/plugins/minesweeper/README.md
index eb1ebcad2..28176bd5e 100644
--- a/applications/plugins/minesweeper/README.md
+++ b/applications/plugins/minesweeper/README.md
@@ -1,5 +1,7 @@
# Minesweeper
+[Original Link](https://github.com/panki27/minesweeper)
+
This is a Minesweeper implementation for the Flipper Zero device.

diff --git a/applications/plugins/minesweeper/minesweeper.c b/applications/plugins/minesweeper/minesweeper.c
index 75fb32e56..43e8f027e 100644
--- a/applications/plugins/minesweeper/minesweeper.c
+++ b/applications/plugins/minesweeper/minesweeper.c
@@ -55,6 +55,7 @@ typedef struct {
int flags_set;
bool game_started;
uint32_t game_started_tick;
+ FuriMutex* mutex;
} Minesweeper;
static void timer_callback(void* ctx) {
@@ -72,10 +73,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
}
static void render_callback(Canvas* const canvas, void* ctx) {
- const Minesweeper* minesweeper_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(minesweeper_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const Minesweeper* minesweeper_state = ctx;
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
+
FuriString* mineStr;
FuriString* timeStr;
mineStr = furi_string_alloc();
@@ -160,7 +161,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
furi_string_free(mineStr);
furi_string_free(timeStr);
- release_mutex((ValueMutex*)ctx, minesweeper_state);
+ furi_mutex_release(minesweeper_state->mutex);
}
static void setup_playfield(Minesweeper* minesweeper_state) {
@@ -217,6 +218,10 @@ static bool game_lost(Minesweeper* minesweeper_state) {
dialog_message_set_icon(message, NULL, 0, 10);
+ // Set cursor to initial position
+ minesweeper_state->cursor_x = 0;
+ minesweeper_state->cursor_y = 0;
+
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message(notifications, &sequence_set_vibro_on);
furi_record_close(RECORD_NOTIFICATION);
@@ -387,8 +392,8 @@ int32_t minesweeper_app(void* p) {
// setup
minesweeper_state_init(minesweeper_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, minesweeper_state, sizeof(minesweeper_state))) {
+ minesweeper_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!minesweeper_state->mutex) {
FURI_LOG_E("Minesweeper", "cannot create mutex\r\n");
free(minesweeper_state);
return 255;
@@ -397,18 +402,19 @@ int32_t minesweeper_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, minesweeper_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
- minesweeper_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, &state_mutex);
+ minesweeper_state->timer =
+ furi_timer_alloc(timer_callback, FuriTimerTypeOnce, minesweeper_state);
// Open GUI and register view_port
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
@@ -472,7 +478,7 @@ int32_t minesweeper_app(void* p) {
// Exit the plugin
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
} else if(event.input.type == InputTypeLong) {
@@ -491,24 +497,21 @@ int32_t minesweeper_app(void* p) {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
}
}
- } else {
- // event timeout
- ;
}
view_port_update(view_port);
- release_mutex(&state_mutex, minesweeper_state);
+ furi_mutex_release(minesweeper_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
- furi_record_close("gui");
+ furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(minesweeper_state->mutex);
furi_timer_free(minesweeper_state->timer);
free(minesweeper_state);
diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam
index 73ad2ba81..d5a2e6c9a 100644
--- a/applications/plugins/morse_code/application.fam
+++ b/applications/plugins/morse_code/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Morse_Code",
+ appid="morse_code",
name="Morse Code",
apptype=FlipperAppType.EXTERNAL,
entry_point="morse_code_app",
@@ -10,5 +10,5 @@ App(
stack_size=1 * 1024,
order=20,
fap_icon="morse_code_10px.png",
- fap_category="Music",
+ fap_category="Misc",
)
diff --git a/applications/plugins/morse_code/morse_code.c b/applications/plugins/morse_code/morse_code.c
index beb661222..3f96969e7 100644
--- a/applications/plugins/morse_code/morse_code.c
+++ b/applications/plugins/morse_code/morse_code.c
@@ -44,15 +44,10 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64);
canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h);
- //dit bpm
- canvas_draw_str_aligned(
- canvas,
- 0,
- 10,
- AlignLeft,
- AlignCenter,
- furi_string_get_cstr(
- furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta)));
+ //dit bpms
+ FuriString* ditbpm = furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta);
+ canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(ditbpm));
+ furi_string_free(ditbpm);
//button info
elements_button_center(canvas, "Press/Hold");
@@ -67,7 +62,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
static void morse_code_worker_callback(FuriString* words, void* context) {
MorseCode* morse_code = context;
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
- morse_code->model->words = words;
+ furi_string_set(morse_code->model->words, words);
furi_mutex_release(morse_code->model_mutex);
view_port_update(morse_code->view_port);
}
@@ -109,6 +104,7 @@ void morse_code_free(MorseCode* instance) {
furi_mutex_free(instance->model_mutex);
+ furi_string_free(instance->model->words);
free(instance->model);
free(instance);
}
@@ -116,10 +112,12 @@ void morse_code_free(MorseCode* instance) {
int32_t morse_code_app() {
MorseCode* morse_code = morse_code_alloc();
InputEvent input;
+
morse_code_worker_start(morse_code->worker);
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
+
while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) ==
FuriStatusOk) {
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
@@ -128,6 +126,7 @@ int32_t morse_code_app() {
break;
} else if(input.key == InputKeyBack && input.type == InputTypeShort) {
morse_code_worker_reset_text(morse_code->worker);
+ furi_string_reset(morse_code->model->words);
} else if(input.key == InputKeyOk) {
if(input.type == InputTypePress)
morse_code_worker_play(morse_code->worker, true);
@@ -160,6 +159,7 @@ int32_t morse_code_app() {
furi_mutex_release(morse_code->model_mutex);
view_port_update(morse_code->view_port);
}
+
morse_code_worker_stop(morse_code->worker);
morse_code_free(morse_code);
return 0;
diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/plugins/morse_code/morse_code_worker.c
index 142b427b6..47896b91a 100644
--- a/applications/plugins/morse_code/morse_code_worker.c
+++ b/applications/plugins/morse_code/morse_code_worker.c
@@ -35,6 +35,8 @@ void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration)
furi_string_push_back(instance->buffer, *DOT);
else if(duration <= (instance->dit_delta * 3))
furi_string_push_back(instance->buffer, *LINE);
+ else
+ furi_string_reset(instance->buffer);
if(furi_string_size(instance->buffer) > 5) furi_string_reset(instance->buffer);
FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer));
}
@@ -87,9 +89,13 @@ static int32_t morse_code_worker_thread_callback(void* context) {
if(!pushed) {
if(end_tick + (instance->dit_delta * 3) < furi_get_tick()) {
//NEW LETTER
- morse_code_worker_fill_letter(instance);
- if(instance->callback)
- instance->callback(instance->words, instance->callback_context);
+ if(!furi_string_empty(instance->buffer)) {
+ morse_code_worker_fill_letter(instance);
+ if(instance->callback)
+ instance->callback(instance->words, instance->callback_context);
+ } else {
+ spaced = true;
+ }
pushed = true;
}
}
@@ -170,6 +176,7 @@ void morse_code_worker_start(MorseCodeWorker* instance) {
void morse_code_worker_stop(MorseCodeWorker* instance) {
furi_assert(instance);
furi_assert(instance->is_running == true);
+ instance->play = false;
instance->is_running = false;
furi_thread_join(instance->thread);
FURI_LOG_D("MorseCode: Stop", "Stop");
diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam
deleted file mode 100644
index 6115315f5..000000000
--- a/applications/plugins/mouse_jiggler/application.fam
+++ /dev/null
@@ -1,12 +0,0 @@
-App(
- appid="MouseJiggler",
- name="Mouse Jiggler",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="mouse_jiggler_app",
- cdefines=["APP_MOUSE_JIGGLER"],
- requires=["gui"],
- stack_size=1 * 1024,
- order=150,
- fap_icon="mouse_10px.png",
- fap_category="Misc",
-)
diff --git a/applications/plugins/mouse_jiggler/mouse_10px.png b/applications/plugins/mouse_jiggler/mouse_10px.png
deleted file mode 100644
index 94c3a7a14..000000000
Binary files a/applications/plugins/mouse_jiggler/mouse_10px.png and /dev/null differ
diff --git a/applications/plugins/mouse_jiggler/mouse_jiggler.c b/applications/plugins/mouse_jiggler/mouse_jiggler.c
deleted file mode 100644
index 868082eea..000000000
--- a/applications/plugins/mouse_jiggler/mouse_jiggler.c
+++ /dev/null
@@ -1,141 +0,0 @@
-#include
-#include
-#include
-#include
-
-#define MOUSE_MOVE_SHORT 5
-#define MOUSE_MOVE_LONG 20
-
-typedef enum {
- EventTypeInput,
- EventTypeKey,
-} EventType;
-
-typedef struct {
- EventType type;
- InputEvent input;
-} UsbMouseEvent;
-
-typedef struct {
- bool running;
-} MouseJigglerState;
-
-static void mouse_jiggler_render_callback(Canvas* canvas, void* ctx) {
- const MouseJigglerState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
-
- canvas_set_font(canvas, FontPrimary);
- canvas_draw_str(canvas, 2, 12, "USB Mouse Jiggler");
- if(!plugin_state->running) {
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 27, " -> STOPPED");
- canvas_draw_str(canvas, 2, 51, "Press [ok] to start");
- canvas_draw_str(canvas, 2, 63, "Press [back] to exit");
- } else {
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 27, " -> RUNNING");
- canvas_draw_str(canvas, 2, 51, "Press [back] to stop");
- }
-
- release_mutex((ValueMutex*)ctx, plugin_state);
-}
-
-static void mouse_jiggler_input_callback(InputEvent* input_event, void* ctx) {
- FuriMessageQueue* event_queue = ctx;
- furi_assert(event_queue);
-
- UsbMouseEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
-
-static void mouse_jiggler_state_init(MouseJigglerState* const plugin_state) {
- plugin_state->running = false;
-}
-
-int32_t mouse_jiggler_app(void* p) {
- UNUSED(p);
-
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent));
-
- MouseJigglerState* plugin_state = malloc(sizeof(MouseJigglerState));
- if(plugin_state == NULL) {
- FURI_LOG_E("MouseJiggler", "MouseJigglerState: malloc error\r\n");
- return 255;
- }
- mouse_jiggler_state_init(plugin_state);
-
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(MouseJigglerState))) {
- FURI_LOG_E("MouseJiggler", "cannot create mutex\r\n");
- furi_message_queue_free(event_queue);
- free(plugin_state);
- return 255;
- }
-
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, mouse_jiggler_render_callback, &state_mutex);
- view_port_input_callback_set(view_port, mouse_jiggler_input_callback, event_queue);
-
- FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
- furi_hal_usb_set_config(&usb_hid, NULL);
-
- // Open GUI and register view_port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
- UsbMouseEvent event;
- //bool status = 0;
-
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
-
- MouseJigglerState* plugin_state = (MouseJigglerState*)acquire_mutex_block(&state_mutex);
-
- if(event_status == FuriStatusOk) {
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypePress) {
- switch(event.input.key) {
- case InputKeyOk:
- if(!plugin_state->running) {
- plugin_state->running = true;
- }
- break;
- case InputKeyBack:
- if(!plugin_state->running) {
- processing = false;
- } else {
- plugin_state->running = false;
- }
- break;
- default:
- break;
- }
- }
- }
- }
-
- if(plugin_state->running) {
- furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);
- furi_delay_ms(500);
- furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);
- furi_delay_ms(500);
- }
-
- view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
- }
-
- furi_hal_usb_set_config(usb_mode_prev, NULL);
-
- // remove & free all stuff created by app
- view_port_enabled_set(view_port, false);
- gui_remove_view_port(gui, view_port);
- furi_record_close(RECORD_GUI);
- view_port_free(view_port);
- furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
-
- return 0;
-}
diff --git a/applications/plugins/mousejacker/README.md b/applications/plugins/mousejacker/README.md
deleted file mode 100644
index ad5fdecdc..000000000
--- a/applications/plugins/mousejacker/README.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# flipperzero-nrf24
-
-An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional.
-
-## Warning
-This repo contains two Flipper Zero apps that utilize the NRF24 driver to sniff for NRF24 addresses and perform mousejack attacks. These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
-
-## Acknowledgments
-The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
-
-The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
-
-Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
-Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
-
-
-## PinOut from from NoComp/Frog
-
-
-# Mousejack / NRF24 pinout by UberGuidoZ
-2/A7 on FZ goes to MOSI/6 on nrf24l01
-3/A6 on FZ goes to MISO/7 on nrf24l01
-4/A4 on FZ goes to CSN/4 on nrf24l01
-5/B3 on FZ goes to SCK/5 on nrf24l01
-6/B2 on FZ goes to CE/3 on nrf24l01
-8/GND on FZ goes to GND/1 on nrf24l01
-9/3V3 on FZ goes to VCC/2 on nrf24l01
-IRQ/8 is left disconnected on nrf24l01
-
-
-
-If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯
-
-
-
-Selfmade NRF24 breakoutboard:
-
-
-
-## Practical hints from einstein2150
-If you are not successfull with the NRF Sniff Plugin you can try to get the MAC of the device with the crazyradio pa USB dongle. Have a look here: https://github.com/SySS-Research/nrf24-playset
-
-
-A sample output of the NRF-Research script could be like
-```
-[2022-08-05 13:28:56.366] Found nRF24 device with address 38:24:93:C0:07 on channel 75\
-[2022-08-05 13:28:56.371] Checking communication\
-[2022-08-05 13:28:59.088] Scan for nRF24 device\
-[2022-08-05 13:28:59.097] Actively searching for address 07:C0:93:24:38\
-```
-
-Be carefull with the byte-order using in mousejacker! The correct byte order is the reverse-byteorder. In my example the reversed one is ```07:C0:93:24:38``` for my Logitech R400 presenter.
-The next thing which could make trouble is the datarate. In my case the Logitech-device is only responding at 2Mbit. Commands at 1Mbit are not detected.
-
-Now its time to write all the relevant information in the config file. In my case you are creating a file ```addresses.txt``` in the SD directory of ```/nrfsniff```. The file content corresponding to the results of the research is ```07C0932438,2``` representing the reversed byte order of the MAC and the comma-separated datarate.
-
-Start the Mouse Jacker plugin and select the prepared ```addresses.txt```. If everything is alright you are starting with your MAC ready for attack:
-
-
-
-If you have troube you can check if a datarate of 1 Mbit will help. Change ```07C0932438,2``` to ```07C0932438,1``` in this case. Another problem in the practical use is electromagnetical noise. In my case the external USB-3.1-Hub creating massive noise in the 2,4 Ghz-Frequency around a distance of 5 cm. Try using a long USB connection-cable for the receiver. In my case the response of the attack raises significant because the signals of the NRF-breakoutboard is no more covered with noise.
-
diff --git a/applications/plugins/mousejacker/application.fam b/applications/plugins/mousejacker/application.fam
index 05835700b..725e81ae8 100644
--- a/applications/plugins/mousejacker/application.fam
+++ b/applications/plugins/mousejacker/application.fam
@@ -9,7 +9,7 @@ App(
"dialogs",
],
stack_size=2 * 1024,
- order=50,
+ order=60,
fap_icon="mouse_10px.png",
fap_category="GPIO",
fap_icon_assets="images",
diff --git a/applications/plugins/mousejacker/icons/sub1_10px.png b/applications/plugins/mousejacker/icons/sub1_10px.png
deleted file mode 100644
index 5a25fdf4e..000000000
Binary files a/applications/plugins/mousejacker/icons/sub1_10px.png and /dev/null differ
diff --git a/applications/plugins/mousejacker/images/badkb_10px.png b/applications/plugins/mousejacker/images/badkb_10px.png
deleted file mode 100644
index 037474aa3..000000000
Binary files a/applications/plugins/mousejacker/images/badkb_10px.png and /dev/null differ
diff --git a/applications/plugins/mousejacker/icons/badkb_10px.png b/applications/plugins/mousejacker/images/badusb_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/icons/badkb_10px.png
rename to applications/plugins/mousejacker/images/badusb_10px.png
diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c
index 3bd772889..9ef23983f 100644
--- a/applications/plugins/mousejacker/mousejacker.c
+++ b/applications/plugins/mousejacker/mousejacker.c
@@ -10,8 +10,7 @@
#include
#include
#include "mousejacker_ducky.h"
-#include
-#include "NRF24_Mouse_Jacker_icons.h"
+#include
#define TAG "mousejacker"
#define LOGITECH_MAX_CHANNEL 85
@@ -41,10 +40,10 @@ char target_address_str[12] = "None";
char target_text[30];
static void render_callback(Canvas* const canvas, void* ctx) {
- const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
+
// border around the edge of the screen
canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -83,7 +82,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_str_aligned(canvas, 3, 30, AlignLeft, AlignBottom, "to exit");
}
- release_mutex((ValueMutex*)ctx, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -112,7 +111,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
- &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px);
+ &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px);
browser_options.hide_ext = false;
bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
@@ -184,7 +183,6 @@ static bool process_ducky_file(
mj_process_ducky_script(
nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf, plugin_state);
FURI_LOG_D(TAG, "finished execution");
- DOLPHIN_DEED(getRandomDeed());
loaded = true;
} else {
FURI_LOG_D(TAG, "load failed. file size: %d", file_size);
@@ -291,8 +289,8 @@ int32_t mousejacker_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
mousejacker_state_init(plugin_state);
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
FURI_LOG_E("mousejacker", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
@@ -301,7 +299,7 @@ int32_t mousejacker_app(void* p) {
// Set system callbacks
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
@@ -332,7 +330,7 @@ int32_t mousejacker_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -382,7 +380,7 @@ int32_t mousejacker_app(void* p) {
}
view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
+ furi_mutex_release(plugin_state->mutex);
}
furi_thread_free(plugin_state->mjthread);
@@ -393,6 +391,7 @@ int32_t mousejacker_app(void* p) {
furi_record_close(RECORD_STORAGE);
view_port_free(view_port);
furi_message_queue_free(event_queue);
+ furi_mutex_free(plugin_state->mutex);
free(plugin_state);
return 0;
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c
index b3b04d836..04b0bfaca 100644
--- a/applications/plugins/mousejacker/mousejacker_ducky.c
+++ b/applications/plugins/mousejacker/mousejacker_ducky.c
@@ -3,6 +3,7 @@
static const char ducky_cmd_comment[] = {"REM"};
static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "};
+static const char ducky_cmd_altstring[] = {"ALTSTRING "};
static const char ducky_cmd_repeat[] = {"REPEAT "};
static uint8_t LOGITECH_HID_TEMPLATE[] =
@@ -11,6 +12,10 @@ static uint8_t LOGITECH_HELLO[] = {0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x0
static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B};
uint8_t prev_hid = 0;
+static bool holding_ctrl = false;
+static bool holding_shift = false;
+static bool holding_alt = false;
+static bool holding_gui = false;
#define RT_THRESHOLD 50
#define LOGITECH_MIN_CHANNEL 2
@@ -65,7 +70,10 @@ MJDuckyKey mj_ducky_keys[] = {{" ", 44, 0}, {"!", 30, 2}, {"\""
{"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0},
{"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0},
{"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0},
- {"LEFT", 80, 0}};
+ {"LEFT", 80, 0}, {"NUM 1", 89, 0}, {"NUM 2", 90, 0},
+ {"NUM 3", 91, 0}, {"NUM 4", 92, 0}, {"NUM 5", 93, 0},
+ {"NUM 6", 94, 0}, {"NUM 7", 95, 0}, {"NUM 8", 96, 0},
+ {"NUM 9", 97, 0}, {"NUM 0", 98, 0}};
/*
static bool mj_ducky_get_number(const char* param, uint32_t* val) {
@@ -89,7 +97,8 @@ static uint32_t mj_ducky_get_command_len(const char* line) {
static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) {
//FURI_LOG_D(TAG, "looking up key %s with length %d", key, keylen);
for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) {
- if(!strncmp(mj_ducky_keys[i].name, key, keylen)) {
+ if(strlen(mj_ducky_keys[i].name) == keylen &&
+ !strncmp(mj_ducky_keys[i].name, key, keylen)) {
memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey));
return true;
}
@@ -152,6 +161,30 @@ static void build_hid_packet(uint8_t mod, uint8_t hid, uint8_t* payload) {
checksum(payload, LOGITECH_HID_TEMPLATE_SIZE);
}
+static void release_key(
+ FuriHalSpiBusHandle* handle,
+ uint8_t* addr,
+ uint8_t addr_size,
+ uint8_t rate,
+ PluginState* plugin_state) {
+ // This function release keys currently pressed, but keep pressing special keys
+ // if holding mod keys variable are set to true
+
+ uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
+ build_hid_packet(
+ 0 | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
+ 0,
+ hid_payload);
+ inject_packet(
+ handle,
+ addr,
+ addr_size,
+ rate,
+ hid_payload,
+ LOGITECH_HID_TEMPLATE_SIZE,
+ plugin_state); // empty hid packet
+}
+
static void send_hid_packet(
FuriHalSpiBusHandle* handle,
uint8_t* addr,
@@ -161,24 +194,22 @@ static void send_hid_packet(
uint8_t hid,
PluginState* plugin_state) {
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
- build_hid_packet(0, 0, hid_payload);
- if(hid == prev_hid)
- inject_packet(
- handle,
- addr,
- addr_size,
- rate,
- hid_payload,
- LOGITECH_HID_TEMPLATE_SIZE,
- plugin_state); // empty hid packet
+ if(hid == prev_hid) release_key(handle, addr, addr_size, rate, plugin_state);
prev_hid = hid;
- build_hid_packet(mod, hid, hid_payload);
+ build_hid_packet(
+ mod | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
+ hid,
+ hid_payload);
inject_packet(
handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE, plugin_state);
furi_delay_ms(12);
}
+static bool ducky_end_line(const char chr) {
+ return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
+}
+
// returns false if there was an error processing script line
static bool mj_process_ducky_line(
FuriHalSpiBusHandle* handle,
@@ -251,6 +282,32 @@ static bool mj_process_ducky_line(
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
}
+ return true;
+ } else if(strncmp(line_tmp, ducky_cmd_altstring, strlen(ducky_cmd_altstring)) == 0) {
+ // ALTSTRING
+ line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
+ for(size_t i = 0; i < strlen(line_tmp); i++) {
+ if((line_tmp[i] < ' ') || (line_tmp[i] > '~')) {
+ continue; // Skip non-printable chars
+ }
+
+ char alt_code[4];
+ // Getting altcode of the char
+ snprintf(alt_code, 4, "%u", line_tmp[i]);
+
+ uint8_t j = 0;
+ while(!ducky_end_line(alt_code[j])) {
+ char pad_num[5] = {'N', 'U', 'M', ' ', alt_code[j]};
+ if(!mj_get_ducky_key(pad_num, 5, &dk)) return false;
+ holding_alt = true;
+ FURI_LOG_D(TAG, "Sending %s", pad_num);
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ j++;
+ }
+ holding_alt = false;
+ release_key(handle, addr, addr_size, rate, plugin_state);
+ }
+
return true;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT
@@ -269,7 +326,9 @@ static bool mj_process_ducky_line(
} else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4, dk.hid, plugin_state);
+ holding_alt = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_alt = false;
return true;
} else if(
strncmp(line_tmp, "GUI", strlen("GUI")) == 0 ||
@@ -277,33 +336,47 @@ static bool mj_process_ducky_line(
strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 8, dk.hid, plugin_state);
+ holding_gui = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_gui = false;
return true;
} else if(
strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 ||
strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 1, dk.hid, plugin_state);
+ holding_ctrl = true;
+ holding_alt = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
+ holding_alt = false;
return true;
} else if(
strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 ||
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state);
+ holding_ctrl = true;
+ holding_shift = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
+ holding_shift = false;
return true;
} else if(
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||
strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1, dk.hid, plugin_state);
+ holding_ctrl = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
return true;
} else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 2, dk.hid, plugin_state);
+ holding_shift = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_shift = false;
return true;
} else if(
strncmp(line_tmp, "ESC", strlen("ESC")) == 0 ||
@@ -344,6 +417,10 @@ static bool mj_process_ducky_line(
if(!mj_get_ducky_key("SPACE", 5, &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
return true;
+ } else if(strncmp(line_tmp, "TAB", strlen("TAB")) == 0) {
+ if(!mj_get_ducky_key("TAB", 3, &dk)) return false;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ return true;
}
return false;
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.h b/applications/plugins/mousejacker/mousejacker_ducky.h
index b4922ed38..e1a422ea7 100644
--- a/applications/plugins/mousejacker/mousejacker_ducky.h
+++ b/applications/plugins/mousejacker/mousejacker_ducky.h
@@ -21,6 +21,7 @@ typedef struct {
} MJDuckyKey;
typedef struct {
+ FuriMutex* mutex;
bool ducky_err;
bool addr_err;
bool is_thread_running;
diff --git a/applications/plugins/multi_converter/multi_converter.c b/applications/plugins/multi_converter/multi_converter.c
index 590730357..bd2b62587 100644
--- a/applications/plugins/multi_converter/multi_converter.c
+++ b/applications/plugins/multi_converter/multi_converter.c
@@ -8,10 +8,9 @@
#include "multi_converter_mode_select.h"
static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
- const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(multi_converter_state == NULL) {
- return;
- }
+ furi_assert(ctx);
+ const MultiConverterState* multi_converter_state = ctx;
+ furi_mutex_acquire(multi_converter_state->mutex, FuriWaitForever);
if(multi_converter_state->mode == ModeDisplay) {
multi_converter_mode_display_draw(canvas, multi_converter_state);
@@ -19,7 +18,7 @@ static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
multi_converter_mode_select_draw(canvas, multi_converter_state);
}
- release_mutex((ValueMutex*)ctx, multi_converter_state);
+ furi_mutex_release(multi_converter_state->mutex);
}
static void
@@ -62,8 +61,8 @@ int32_t multi_converter_app(void* p) {
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
// set mutex for plugin state (different threads can access it)
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
+ multi_converter_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!multi_converter_state->mutex) {
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(multi_converter_state);
@@ -72,11 +71,11 @@ int32_t multi_converter_app(void* p) {
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
+ view_port_draw_callback_set(view_port, multi_converter_render_callback, multi_converter_state);
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
// open GUI and register view_port
- Gui* gui = furi_record_open("gui");
+ Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
multi_converter_init(multi_converter_state);
@@ -85,8 +84,7 @@ int32_t multi_converter_app(void* p) {
MultiConverterEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- MultiConverterState* multi_converter_state =
- (MultiConverterState*)acquire_mutex_block(&state_mutex);
+ furi_mutex_acquire(multi_converter_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -148,20 +146,18 @@ int32_t multi_converter_app(void* p) {
} else if(multi_converter_state->keyboard_lock) {
multi_converter_state->keyboard_lock = 0;
}
- } else {
- // event timeout
}
view_port_update(view_port);
- release_mutex(&state_mutex, multi_converter_state);
+ furi_mutex_release(multi_converter_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
- furi_record_close("gui");
+ furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
- delete_mutex(&state_mutex);
+ furi_mutex_free(multi_converter_state->mutex);
free(multi_converter_state);
return 0;
diff --git a/applications/plugins/multi_converter/multi_converter_definitions.h b/applications/plugins/multi_converter/multi_converter_definitions.h
index 3bed192a0..2f79bc7d9 100644
--- a/applications/plugins/multi_converter/multi_converter_definitions.h
+++ b/applications/plugins/multi_converter/multi_converter_definitions.h
@@ -70,6 +70,7 @@ struct MultiConverterUnit {
};
struct MultiConverterState {
+ FuriMutex* mutex;
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
MultiConverterUnitType unit_type_orig;
diff --git a/applications/plugins/dice/application.fam b/applications/plugins/multi_dice/application.fam
similarity index 90%
rename from applications/plugins/dice/application.fam
rename to applications/plugins/multi_dice/application.fam
index bd836c48c..15f5fe820 100644
--- a/applications/plugins/dice/application.fam
+++ b/applications/plugins/multi_dice/application.fam
@@ -1,5 +1,5 @@
App(
- appid="Dice",
+ appid="multi_dice",
name="Multi-Dice",
apptype=FlipperAppType.EXTERNAL,
entry_point="dice_app",
diff --git a/applications/plugins/dice/dice.c b/applications/plugins/multi_dice/dice.c
similarity index 98%
rename from applications/plugins/dice/dice.c
rename to applications/plugins/multi_dice/dice.c
index dc748b68f..2de407cf0 100644
--- a/applications/plugins/dice/dice.c
+++ b/applications/plugins/multi_dice/dice.c
@@ -4,7 +4,6 @@
#include
#include