Merge branch 'dev' into subghz/code_improvement
14
.drone.yml
@ -91,7 +91,7 @@ steps:
|
||||
- echo '' >> CHANGELOG.md
|
||||
- echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md
|
||||
- echo '' >> CHANGELOG.md
|
||||
- echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md
|
||||
- echo '### [Version with RGB patch - only for hardware mod! - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md
|
||||
- echo '' >> CHANGELOG.md
|
||||
- echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md
|
||||
environment:
|
||||
@ -252,10 +252,10 @@ steps:
|
||||
[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n)
|
||||
|
||||
|
||||
[-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r)
|
||||
[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r)
|
||||
|
||||
|
||||
[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz)
|
||||
[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz)
|
||||
|
||||
|
||||
[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)"
|
||||
@ -271,7 +271,7 @@ steps:
|
||||
commands:
|
||||
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||
- chmod +x ./discord.sh
|
||||
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
|
||||
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
|
||||
|
||||
- name: "Send extra pack build to telegram"
|
||||
image: appleboy/drone-telegram
|
||||
@ -474,10 +474,10 @@ steps:
|
||||
[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})
|
||||
|
||||
|
||||
[-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r)
|
||||
[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r)
|
||||
|
||||
|
||||
[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz)
|
||||
[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz)
|
||||
|
||||
|
||||
[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)"
|
||||
@ -515,7 +515,7 @@ steps:
|
||||
commands:
|
||||
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||
- chmod +x ./discord.sh
|
||||
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')'
|
||||
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')'
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
|
76
CHANGELOG.md
@ -1,46 +1,17 @@
|
||||
### New changes
|
||||
* If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed!
|
||||
* Settings->LCD and Notifications will be resetted to default due to new Contrast setting from OFW
|
||||
* Core2 (Crash in idle) issues was reduced to current possible minimum, you can try using DeepSleep again (Sleep Method = Default) (+ more checks was added, if you get `Slow HSE/PLL startup` message more than one time, create issue with steps what to do to reproduce it again)
|
||||
-----
|
||||
* Plugins: **New RFID 125KHz and iButton Fuzzers (remake from scratch + new features)** (by @gid9798 | PR #507)
|
||||
* Plugins: SubGHz Bruteforcer -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + allow more repeats (by @gid9798 & @xMasterX)
|
||||
* Plugins: Update TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
|
||||
* Plugins: Unitemp SCD30 support (PR in unitemp repo by @divinebird / fixed by @xMasterX)
|
||||
* Plugins: Fix ProtoView issue #503 -> (Broken saved files with custom modulation)
|
||||
* SubGHz: Added 430, 431 MHz to default list
|
||||
* SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506)
|
||||
* SubGHz: Notifications fixes (by @wosk | PR #464)
|
||||
* GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509)
|
||||
* CI/CD: Provide builds with RGB patch for modded flippers (with special led board installed)
|
||||
* Infrared: `RCA` protocol support
|
||||
* Infrared: Update universal remote assets - add new ACs and TCL TV
|
||||
* API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502)
|
||||
* OFW PR 2760: NFC: Improvements to NFC Magic app (by AloneLiberty)
|
||||
* OFW PR 2756: fix: make dialog_file_browser_set_basic_options initialize all fields (by JarvisCraft)
|
||||
* OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys
|
||||
* OFW: fbt: stable build dates
|
||||
* OFW: weather_station: add oregon3 with THGR221
|
||||
* OFW: Services: simplify api (DOLPHIN_DEED->dolphin_deed - function instead of macros + remake all apps in extra pack and main fw to use new API) -> **Breaking API change, API version was changed from 29.x to 30.x**
|
||||
* OFW: Core2, SRAM2: provide safety gap
|
||||
* OFW: FuriHal: always clock SMPS from HSI
|
||||
* OFW: ble: refactored bt gatt characteristics setup (+ remake of BT HID Led descriptor in new way to work with this changes)
|
||||
* OFW: Scripts: WiFi board updater
|
||||
* OFW: github: re-enabled f18 build
|
||||
* OFW: added ISO15693 (NfcV) (was already added before, so we just updated it with latest changes)
|
||||
* OFW: fbt: added Flipper selection when multiple are connected over USB
|
||||
* OFW: fbt, ufbt: added checks for appid in app manifests
|
||||
* OFW: Fix core2 permisions
|
||||
* OFW: SubGhz: add subghz_protocol_registry external API (was already in our API but in different way)
|
||||
* OFW: Furi: smaller critical enter and critical exit macro
|
||||
* OFW: Serial_CLI: Fixing serial cli logger error so it sounds more concise
|
||||
* OFW: Remove unused resources
|
||||
* OFW: Dolphin: new animation
|
||||
* OFW: f7: add PB9 to debug pins
|
||||
* OFW: Settings: add contrast adjustment -> **Settings->LCD and Notifications will be resetted to default values one time after installing**
|
||||
* OFW: FuriHal: add system setting to device info, bump device info version
|
||||
## New changes
|
||||
* OFW PR: Update OFW PR 2782
|
||||
* OFW: Add Mitsubishi MSZ-AP25VGK universal ac remote
|
||||
* OFW: Fix roll-over in file browser and archive
|
||||
* OFW: Fix fr-FR-mac keylayout
|
||||
* OFW: NFC/RFID detector app
|
||||
* OFW: Fast FAP Loader
|
||||
* OFW: LF-RFID debug: make it work
|
||||
* OFW: Fix M*LIB usage
|
||||
* OFW: fix: make `dialog_file_browser_set_basic_options` initialize all fields
|
||||
* OFW: Scroll acceleration
|
||||
* OFW: Loader refaptoring: second encounter
|
||||
|
||||
#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)
|
||||
----
|
||||
|
||||
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
@ -59,19 +30,26 @@
|
||||
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
|
||||
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
|
||||
|
||||
### Thanks to our sponsors:
|
||||
#### Thanks to our sponsors:
|
||||
callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
|
||||
and all other great people who supported our project and me (xMasterX), thanks to you all!
|
||||
|
||||
**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed**
|
||||
|
||||
**Recommended option - Web Updater**
|
||||
## **Recommended update option - Web Updater**
|
||||
|
||||
### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater?
|
||||
What means `n` or `e` in - `flipper-z-f7-update-(version)(n / r / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations,
|
||||
`e` means build has extra apps pack preinstalled,
|
||||
`r` means RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!)
|
||||
What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ? <br>
|
||||
`flipper-z` = for Flipper Zero device<br>
|
||||
`f7` = Hardware version - same for all flipper zero devices<br>
|
||||
`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself<br>
|
||||
`(version)` = Firmware version<br>
|
||||
`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations<br>
|
||||
`e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled<br>
|
||||
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!)
|
||||
|
||||
Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web<br>
|
||||
Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`<br>
|
||||
SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip`
|
||||
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper / web
|
||||
|
||||
|
||||
|
@ -48,7 +48,7 @@ Almost everything in flipper firmware is built around this concept.
|
||||
# C coding style
|
||||
|
||||
- Tab is 4 spaces
|
||||
- Use `fbt format` to reformat source code and check style guide before commit
|
||||
- Use `./fbt format` to reformat source code and check style guide before commit
|
||||
|
||||
## Naming
|
||||
|
||||
|
10
ReadMe.md
@ -6,15 +6,15 @@
|
||||
|
||||
### Welcome to the Flipper Zero Unleashed Firmware repo!
|
||||
|
||||
**This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
|
||||
#### **This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
|
||||
|
||||
<br>
|
||||
|
||||
Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
|
||||
### Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
|
||||
|
||||
<br>
|
||||
|
||||
### This software is for experimental purposes only and is not meant for any illegal activity/purposes. <br> We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. <br> Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
|
||||
##### This software is for experimental purposes only and is not meant for any illegal activity/purposes. <br> We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. <br> Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
|
||||
|
||||
<br>
|
||||
Our Discord Community:
|
||||
@ -79,7 +79,7 @@ Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported man
|
||||
|
||||
Encoders or sending made by @xMasterX:
|
||||
- Nero Radio 57bit (+ 56bit encoder improvements)
|
||||
- CAME 12bit/24bit encoder fixes (already merged in OFW)
|
||||
- CAME 12bit/24bit encoder fixes (Fixes now merged in OFW)
|
||||
- Keeloq: HCS101
|
||||
- Keeloq: AN-Motors
|
||||
- Keeloq: JCM Tech
|
||||
@ -97,7 +97,7 @@ Encoders or sending made by @xMasterX:
|
||||
Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version):
|
||||
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: @mmx7)]
|
||||
- Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
|
||||
- Star Line
|
||||
- Security+ v1 & v2 (encoders was made in OFW)
|
||||
|
@ -26,7 +26,6 @@ Applications for main Flipper menu.
|
||||
|
||||
- `archive` - Archive and file manager
|
||||
- `bad_usb` - Bad USB application
|
||||
- `fap_loader` - External applications loader
|
||||
- `gpio` - GPIO application: includes USART bridge and GPIO control
|
||||
- `ibutton` - iButton application, onewire keys and more
|
||||
- `infrared` - Infrared application, controls your IR devices
|
||||
|
@ -12,5 +12,6 @@ App(
|
||||
"display_test",
|
||||
"text_box_test",
|
||||
"file_browser_test",
|
||||
"speaker_debug",
|
||||
],
|
||||
)
|
||||
|
10
applications/debug/crash_test/application.fam
Normal file
@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="crash_test",
|
||||
name="Crash Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="crash_test_app",
|
||||
cdefines=["APP_CRASH_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Debug",
|
||||
)
|
128
applications/debug/crash_test/crash_test.c
Normal file
@ -0,0 +1,128 @@
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#define TAG "CrashTest"
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
} CrashTest;
|
||||
|
||||
typedef enum {
|
||||
CrashTestViewSubmenu,
|
||||
} CrashTestView;
|
||||
|
||||
typedef enum {
|
||||
CrashTestSubmenuCheck,
|
||||
CrashTestSubmenuCheckMessage,
|
||||
CrashTestSubmenuAssert,
|
||||
CrashTestSubmenuAssertMessage,
|
||||
CrashTestSubmenuCrash,
|
||||
CrashTestSubmenuHalt,
|
||||
} CrashTestSubmenu;
|
||||
|
||||
static void crash_test_submenu_callback(void* context, uint32_t index) {
|
||||
CrashTest* instance = (CrashTest*)context;
|
||||
UNUSED(instance);
|
||||
|
||||
switch(index) {
|
||||
case CrashTestSubmenuCheck:
|
||||
furi_check(false);
|
||||
break;
|
||||
case CrashTestSubmenuCheckMessage:
|
||||
furi_check(false, "Crash test: furi_check with message");
|
||||
break;
|
||||
case CrashTestSubmenuAssert:
|
||||
furi_assert(false);
|
||||
break;
|
||||
case CrashTestSubmenuAssertMessage:
|
||||
furi_assert(false, "Crash test: furi_assert with message");
|
||||
break;
|
||||
case CrashTestSubmenuCrash:
|
||||
furi_crash("Crash test: furi_crash");
|
||||
break;
|
||||
case CrashTestSubmenuHalt:
|
||||
furi_halt("Crash test: furi_halt");
|
||||
break;
|
||||
default:
|
||||
furi_crash("Programming error");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t crash_test_exit_callback(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
CrashTest* crash_test_alloc() {
|
||||
CrashTest* instance = malloc(sizeof(CrashTest));
|
||||
|
||||
View* view = NULL;
|
||||
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Menu
|
||||
instance->submenu = submenu_alloc();
|
||||
view = submenu_get_view(instance->submenu);
|
||||
view_set_previous_callback(view, crash_test_exit_callback);
|
||||
view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Check with message",
|
||||
CrashTestSubmenuCheckMessage,
|
||||
crash_test_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Assert with message",
|
||||
CrashTestSubmenuAssertMessage,
|
||||
crash_test_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance);
|
||||
submenu_add_item(
|
||||
instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void crash_test_free(CrashTest* instance) {
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu);
|
||||
submenu_free(instance->submenu);
|
||||
|
||||
view_dispatcher_free(instance->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
int32_t crash_test_run(CrashTest* instance) {
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu);
|
||||
view_dispatcher_run(instance->view_dispatcher);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t crash_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
CrashTest* instance = crash_test_alloc();
|
||||
|
||||
int32_t ret = crash_test_run(instance);
|
||||
|
||||
crash_test_free(instance);
|
||||
|
||||
return ret;
|
||||
}
|
@ -6,6 +6,11 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) {
|
||||
furi_hal_gpio_write(&gpio_ext_pa7, !level);
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_callback(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA);
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
|
||||
@ -16,6 +21,8 @@ void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
|
||||
furi_hal_rfid_tim_read_start(125000, 0.5);
|
||||
|
||||
lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ typedef struct {
|
||||
uint32_t ARR;
|
||||
uint32_t CCR;
|
||||
int pos;
|
||||
void (*update_callback)(void* context);
|
||||
void* update_context;
|
||||
} LfRfidTuneViewModel;
|
||||
|
||||
static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) {
|
||||
@ -151,6 +153,18 @@ static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* conte
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(event->key == InputKeyLeft || event->key == InputKeyRight) {
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->update_callback) {
|
||||
model->update_callback(model->update_context);
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
@ -161,19 +175,7 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() {
|
||||
tune_view->view = view_alloc();
|
||||
view_set_context(tune_view->view, tune_view);
|
||||
view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel));
|
||||
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
},
|
||||
true);
|
||||
|
||||
lfrfid_debug_view_tune_clean(tune_view);
|
||||
view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback);
|
||||
view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback);
|
||||
|
||||
@ -199,6 +201,8 @@ void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) {
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
model->update_callback = NULL;
|
||||
model->update_context = NULL;
|
||||
},
|
||||
true);
|
||||
}
|
||||
@ -232,3 +236,17 @@ uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_set_callback(
|
||||
LfRfidTuneView* tune_view,
|
||||
void (*callback)(void* context),
|
||||
void* context) {
|
||||
with_view_model(
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->update_callback = callback;
|
||||
model->update_context = context;
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
@ -16,3 +16,8 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view);
|
||||
uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view);
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view);
|
||||
|
||||
void lfrfid_debug_view_tune_set_callback(
|
||||
LfRfidTuneView* tune_view,
|
||||
void (*callback)(void* context),
|
||||
void* context);
|
||||
|
11
applications/debug/speaker_debug/application.fam
Normal file
@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="speaker_debug",
|
||||
name="Speaker Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="speaker_debug_app",
|
||||
requires=["gui", "notification"],
|
||||
stack_size=2 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
fap_libs=["music_worker"],
|
||||
)
|
120
applications/debug/speaker_debug/speaker_debug.c
Normal file
@ -0,0 +1,120 @@
|
||||
#include <furi.h>
|
||||
#include <notification/notification.h>
|
||||
#include <music_worker/music_worker.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#define TAG "SpeakerDebug"
|
||||
#define CLI_COMMAND "speaker_debug"
|
||||
|
||||
typedef enum {
|
||||
SpeakerDebugAppMessageTypeStop,
|
||||
} SpeakerDebugAppMessageType;
|
||||
|
||||
typedef struct {
|
||||
SpeakerDebugAppMessageType type;
|
||||
} SpeakerDebugAppMessage;
|
||||
|
||||
typedef struct {
|
||||
MusicWorker* music_worker;
|
||||
FuriMessageQueue* message_queue;
|
||||
Cli* cli;
|
||||
} SpeakerDebugApp;
|
||||
|
||||
static SpeakerDebugApp* speaker_app_alloc() {
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));
|
||||
app->music_worker = music_worker_alloc();
|
||||
app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));
|
||||
app->cli = furi_record_open(RECORD_CLI);
|
||||
return app;
|
||||
}
|
||||
|
||||
static void speaker_app_free(SpeakerDebugApp* app) {
|
||||
music_worker_free(app->music_worker);
|
||||
furi_message_queue_free(app->message_queue);
|
||||
furi_record_close(RECORD_CLI);
|
||||
free(app);
|
||||
}
|
||||
|
||||
static void speaker_app_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
|
||||
SpeakerDebugApp* app = (SpeakerDebugApp*)context;
|
||||
SpeakerDebugAppMessage message;
|
||||
FuriString* cmd = furi_string_alloc();
|
||||
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
furi_string_free(cmd);
|
||||
printf("Usage:\r\n");
|
||||
printf("\t" CLI_COMMAND " stop\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(furi_string_cmp(cmd, "stop") == 0) {
|
||||
message.type = SpeakerDebugAppMessageTypeStop;
|
||||
FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100);
|
||||
if(status != FuriStatusOk) {
|
||||
printf("Failed to send message\r\n");
|
||||
} else {
|
||||
printf("Stopping\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Usage:\r\n");
|
||||
printf("\t" CLI_COMMAND " stop\r\n");
|
||||
}
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) {
|
||||
if(music_worker_is_playing(app->music_worker)) {
|
||||
music_worker_stop(app->music_worker);
|
||||
}
|
||||
|
||||
if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) {
|
||||
FURI_LOG_E(TAG, "Failed to load RTTTL");
|
||||
return false;
|
||||
}
|
||||
|
||||
music_worker_set_volume(app->music_worker, 1.0f);
|
||||
music_worker_start(app->music_worker);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void speaker_app_music_stop(SpeakerDebugApp* app) {
|
||||
if(music_worker_is_playing(app->music_worker)) {
|
||||
music_worker_stop(app->music_worker);
|
||||
}
|
||||
}
|
||||
|
||||
static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
||||
if(!arg || !speaker_app_music_play(app, arg)) {
|
||||
FURI_LOG_E(TAG, "Provided RTTTL is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
||||
|
||||
SpeakerDebugAppMessage message;
|
||||
FuriStatus status;
|
||||
while(true) {
|
||||
status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever);
|
||||
|
||||
if(status == FuriStatusOk) {
|
||||
if(message.type == SpeakerDebugAppMessageTypeStop) {
|
||||
speaker_app_music_stop(app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cli_delete_command(app->cli, CLI_COMMAND);
|
||||
}
|
||||
|
||||
int32_t speaker_debug_app(void* arg) {
|
||||
SpeakerDebugApp* app = speaker_app_alloc();
|
||||
speaker_app_run(app, arg);
|
||||
speaker_app_free(app);
|
||||
return 0;
|
||||
}
|
@ -10,6 +10,8 @@
|
||||
|
||||
#define LINES_ON_SCREEN 6
|
||||
#define COLUMNS_ON_SCREEN 21
|
||||
#define TAG "UartEcho"
|
||||
#define DEFAULT_BAUD_RATE 230400
|
||||
|
||||
typedef struct UartDumpModel UartDumpModel;
|
||||
|
||||
@ -179,7 +181,7 @@ static int32_t uart_echo_worker(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UartEchoApp* uart_echo_app_alloc() {
|
||||
static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
|
||||
UartEchoApp* app = malloc(sizeof(UartEchoApp));
|
||||
|
||||
app->rx_stream = furi_stream_buffer_alloc(2048, 1);
|
||||
@ -220,7 +222,7 @@ static UartEchoApp* uart_echo_app_alloc() {
|
||||
|
||||
// Enable uart listener
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate);
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||
|
||||
return app;
|
||||
@ -263,8 +265,18 @@ static void uart_echo_app_free(UartEchoApp* app) {
|
||||
}
|
||||
|
||||
int32_t uart_echo_app(void* p) {
|
||||
UNUSED(p);
|
||||
UartEchoApp* app = uart_echo_app_alloc();
|
||||
uint32_t baudrate = DEFAULT_BAUD_RATE;
|
||||
if(p) {
|
||||
const char* baudrate_str = p;
|
||||
if(sscanf(baudrate_str, "%lu", &baudrate) != 1) {
|
||||
FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str);
|
||||
baudrate = DEFAULT_BAUD_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate);
|
||||
|
||||
UartEchoApp* app = uart_echo_app_alloc(baudrate);
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
uart_echo_app_free(app);
|
||||
return 0;
|
||||
|
@ -0,0 +1,32 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
|
||||
MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) {
|
||||
mu_assert(
|
||||
sizeof(DialogsFileBrowserOptions) == 28,
|
||||
"Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`");
|
||||
|
||||
DialogsFileBrowserOptions options;
|
||||
dialog_file_browser_set_basic_options(&options, ".fap", NULL);
|
||||
// note: this assertions can safely be changed, their primary purpose is to remind the maintainer
|
||||
// to update `dialog_file_browser_set_basic_options` by including all structure fields in it
|
||||
mu_assert_string_eq(".fap", options.extension);
|
||||
mu_assert_null(options.base_path);
|
||||
mu_assert(options.skip_assets, "`skip_assets` should default to `true");
|
||||
mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true");
|
||||
mu_assert_null(options.icon);
|
||||
mu_assert(options.hide_ext, "`hide_ext` should default to `true");
|
||||
mu_assert_null(options.item_loader_callback);
|
||||
mu_assert_null(options.item_loader_context);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(dialogs_file_browser_options) {
|
||||
MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields);
|
||||
}
|
||||
|
||||
int run_minunit_test_dialogs_file_browser_options() {
|
||||
MU_RUN_SUITE(dialogs_file_browser_options);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
@ -27,6 +27,7 @@ int run_minunit_test_nfc();
|
||||
int run_minunit_test_bit_lib();
|
||||
int run_minunit_test_float_tools();
|
||||
int run_minunit_test_bt();
|
||||
int run_minunit_test_dialogs_file_browser_options();
|
||||
|
||||
typedef int (*UnitTestEntry)();
|
||||
|
||||
@ -55,6 +56,8 @@ const UnitTest unit_tests[] = {
|
||||
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
||||
{.name = "float_tools", .entry = run_minunit_test_float_tools},
|
||||
{.name = "bt", .entry = run_minunit_test_bt},
|
||||
{.name = "dialogs_file_browser_options",
|
||||
.entry = run_minunit_test_dialogs_file_browser_options},
|
||||
};
|
||||
|
||||
void minunit_print_progress() {
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=30,
|
||||
fap_icon="arkanoid_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="@xMasterX & @gotnull",
|
||||
fap_version="1.0",
|
||||
fap_description="Arkanoid Game",
|
||||
)
|
||||
|
@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
|
||||
AvrIspWorkerRW* instance = context;
|
||||
|
||||
/* start PWM on &gpio_ext_pa4 */
|
||||
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
|
||||
if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
|
||||
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Start");
|
||||
|
||||
@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
|
||||
}
|
||||
FURI_LOG_D(TAG, "Stop");
|
||||
|
||||
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
|
||||
if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
|
||||
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
|
||||
instance->chip_arr_ind = avr_isp_chip_arr_size + 1;
|
||||
|
||||
/* start PWM on &gpio_ext_pa4 */
|
||||
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
|
||||
bool was_pwm_enabled = false;
|
||||
if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
|
||||
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
|
||||
} else {
|
||||
was_pwm_enabled = true;
|
||||
}
|
||||
|
||||
do {
|
||||
if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) {
|
||||
@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
|
||||
|
||||
} while(0);
|
||||
|
||||
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
|
||||
if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) {
|
||||
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
|
||||
}
|
||||
|
||||
if(instance->callback) {
|
||||
if(instance->chip_arr_ind > avr_isp_chip_arr_size) {
|
||||
|
@ -11,4 +11,7 @@ App(
|
||||
order=50,
|
||||
fap_icon="barcode_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_author="@xMasterX & @msvsergey & @McAzzaMan",
|
||||
fap_version="1.0",
|
||||
fap_description="App displays Barcode on flipper screen and allows to edit it",
|
||||
)
|
@ -8,5 +8,8 @@ App(
|
||||
order=30,
|
||||
fap_icon="blackjack_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets"
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@teeebor",
|
||||
fap_version="1.0",
|
||||
fap_description="Blackjack Game",
|
||||
)
|
@ -11,4 +11,7 @@ App(
|
||||
fap_icon="bomb.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@leo-need-more-coffee & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Bomberduck(Bomberman) Game",
|
||||
)
|
||||
|
@ -12,4 +12,7 @@ App(
|
||||
stack_size=8 * 1024,
|
||||
order=20,
|
||||
fap_category="Tools",
|
||||
fap_author="@litui & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=100,
|
||||
fap_icon="wifi_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@SequoiaSan & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="DSTIKE Deauther module interface, based on ESP8266",
|
||||
)
|
||||
|
@ -9,4 +9,7 @@ App(
|
||||
fap_icon="flappy_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@DroomOne & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Flappy Bird Game",
|
||||
)
|
||||
|
@ -9,4 +9,7 @@ App(
|
||||
fap_icon="i2ctools.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="images",
|
||||
fap_author="@NaejEL",
|
||||
fap_version="1.0",
|
||||
fap_description="Set of i2c tools",
|
||||
)
|
3
applications/external/game15/application.fam
vendored
@ -8,4 +8,7 @@ App(
|
||||
fap_icon="game15_10px.png",
|
||||
order=30,
|
||||
fap_category="Games",
|
||||
fap_author="@x27",
|
||||
fap_version="1.0",
|
||||
fap_description="Logic Game",
|
||||
)
|
||||
|
@ -9,5 +9,8 @@ App(
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="game_2048.png",
|
||||
fap_category="Games"
|
||||
fap_category="Games",
|
||||
fap_author="@eugene-kirzhanov",
|
||||
fap_version="1.0",
|
||||
fap_description="2048 Game",
|
||||
)
|
@ -8,4 +8,7 @@ App(
|
||||
order=35,
|
||||
fap_icon="gps_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@ezod & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Works with GPS modules via UART, using NMEA protocol.",
|
||||
)
|
||||
|
@ -10,4 +10,7 @@ App(
|
||||
order=20,
|
||||
fap_icon="dist_sensor10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@xMasterX (first implementation by @Sanqui)",
|
||||
fap_version="1.0",
|
||||
fap_description="HC-SR(04) Distance sensor reader",
|
||||
)
|
@ -8,4 +8,7 @@ App(
|
||||
fap_category="Games",
|
||||
fap_icon="box.png",
|
||||
fap_icon_assets="assets_images",
|
||||
fap_author="@xMasterX (original implementation by @wquinoa & @Vedmein)",
|
||||
fap_version="1.0",
|
||||
fap_description="Heap Defence game from hackathon (aka Stack Attack)",
|
||||
)
|
||||
|
@ -12,4 +12,7 @@ App(
|
||||
fap_icon="icons/hex_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@QtRoS",
|
||||
fap_version="1.0",
|
||||
fap_description="App allows to view various files as HEX.",
|
||||
)
|
||||
|
BIN
applications/external/hid_app/assets/Space_60x18.png
vendored
Normal file
After Width: | Height: | Size: 2.8 KiB |
18
applications/external/hid_app/hid.c
vendored
@ -22,10 +22,12 @@ static void hid_submenu_callback(void* context, uint32_t index) {
|
||||
Hid* app = context;
|
||||
if(index == HidSubmenuIndexKeynote) {
|
||||
app->view_id = HidViewKeynote;
|
||||
hid_keynote_set_orientation(app->hid_keynote, false);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
|
||||
} else if(index == HidSubmenuIndexKeynoteVertical) {
|
||||
app->view_id = HidViewKeynoteVertical;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical);
|
||||
app->view_id = HidViewKeynote;
|
||||
hid_keynote_set_orientation(app->hid_keynote, true);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
|
||||
} else if(index == HidSubmenuIndexKeyboard) {
|
||||
app->view_id = HidViewKeyboard;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
|
||||
@ -62,7 +64,6 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
|
||||
}
|
||||
}
|
||||
hid_keynote_set_connected_status(hid->hid_keynote, connected);
|
||||
hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected);
|
||||
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
|
||||
hid_numpad_set_connected_status(hid->hid_numpad, connected);
|
||||
hid_media_set_connected_status(hid->hid_media, connected);
|
||||
@ -177,15 +178,6 @@ Hid* hid_app_alloc_view(void* context) {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
|
||||
|
||||
// Keynote Vertical view
|
||||
app->hid_keynote_vertical = hid_keynote_vertical_alloc(app);
|
||||
view_set_previous_callback(
|
||||
hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
HidViewKeynoteVertical,
|
||||
hid_keynote_vertical_get_view(app->hid_keynote_vertical));
|
||||
|
||||
// Keyboard view
|
||||
app->hid_keyboard = hid_keyboard_alloc(app);
|
||||
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
|
||||
@ -252,8 +244,6 @@ void hid_free(Hid* app) {
|
||||
dialog_ex_free(app->dialog);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
|
||||
hid_keynote_free(app->hid_keynote);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical);
|
||||
hid_keynote_vertical_free(app->hid_keynote_vertical);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
|
||||
hid_keyboard_free(app->hid_keyboard);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad);
|
||||
|
2
applications/external/hid_app/hid.h
vendored
@ -17,7 +17,6 @@
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "views/hid_keynote.h"
|
||||
#include "views/hid_keynote_vertical.h"
|
||||
#include "views/hid_keyboard.h"
|
||||
#include "views/hid_numpad.h"
|
||||
#include "views/hid_media.h"
|
||||
@ -43,7 +42,6 @@ struct Hid {
|
||||
Submenu* device_type_submenu;
|
||||
DialogEx* dialog;
|
||||
HidKeynote* hid_keynote;
|
||||
HidKeynoteVertical* hid_keynote_vertical;
|
||||
HidKeyboard* hid_keyboard;
|
||||
HidNumpad* hid_numpad;
|
||||
HidMedia* hid_media;
|
||||
|
1
applications/external/hid_app/views.h
vendored
@ -1,7 +1,6 @@
|
||||
typedef enum {
|
||||
HidViewSubmenu,
|
||||
HidViewKeynote,
|
||||
HidViewKeynoteVertical,
|
||||
HidViewKeyboard,
|
||||
HidViewNumpad,
|
||||
HidViewMedia,
|
||||
|
@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
||||
}
|
||||
|
||||
static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidKeynoteModel* model = context;
|
||||
|
||||
// Header
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
|
||||
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
|
||||
} else {
|
||||
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
|
||||
|
||||
const uint8_t x_2 = 23;
|
||||
const uint8_t x_1 = 2;
|
||||
const uint8_t x_3 = 44;
|
||||
|
||||
const uint8_t y_1 = 44;
|
||||
const uint8_t y_2 = 65;
|
||||
|
||||
// Up
|
||||
canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18);
|
||||
if(model->up_pressed) {
|
||||
elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18);
|
||||
if(model->down_pressed) {
|
||||
elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18);
|
||||
if(model->left_pressed) {
|
||||
elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18);
|
||||
if(model->right_pressed) {
|
||||
elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
canvas_draw_icon(canvas, 2, 86, &I_Space_60x18);
|
||||
if(model->ok_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 5, 88, 55, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9);
|
||||
elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
canvas_draw_icon(canvas, 2, 107, &I_Space_60x18);
|
||||
if(model->back_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 5, 109, 55, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8);
|
||||
elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back");
|
||||
}
|
||||
|
||||
static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {
|
||||
with_view_model(
|
||||
hid_keynote->view,
|
||||
@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
|
||||
with_view_model(
|
||||
hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);
|
||||
}
|
||||
|
||||
void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) {
|
||||
furi_assert(hid_keynote);
|
||||
|
||||
if(vertical) {
|
||||
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback);
|
||||
view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip);
|
||||
|
||||
} else {
|
||||
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
|
||||
view_set_orientation(hid_keynote->view, ViewOrientationHorizontal);
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote);
|
||||
View* hid_keynote_get_view(HidKeynote* hid_keynote);
|
||||
|
||||
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected);
|
||||
|
||||
void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical);
|
||||
|
@ -1,228 +0,0 @@
|
||||
#include "hid_keynote_vertical.h"
|
||||
#include <gui/elements.h>
|
||||
#include "../hid.h"
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#define TAG "HidKeynoteVertical"
|
||||
|
||||
struct HidKeynoteVertical {
|
||||
View* view;
|
||||
Hid* hid;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool left_pressed;
|
||||
bool up_pressed;
|
||||
bool right_pressed;
|
||||
bool down_pressed;
|
||||
bool ok_pressed;
|
||||
bool back_pressed;
|
||||
bool connected;
|
||||
HidTransport transport;
|
||||
} HidKeynoteVerticalModel;
|
||||
|
||||
static void
|
||||
hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
||||
if(dir == CanvasDirectionBottomToTop) {
|
||||
canvas_draw_line(canvas, x, y + 6, x, y - 1);
|
||||
} else if(dir == CanvasDirectionTopToBottom) {
|
||||
canvas_draw_line(canvas, x, y - 6, x, y + 1);
|
||||
} else if(dir == CanvasDirectionRightToLeft) {
|
||||
canvas_draw_line(canvas, x + 6, y, x - 1, y);
|
||||
} else if(dir == CanvasDirectionLeftToRight) {
|
||||
canvas_draw_line(canvas, x - 6, y, x + 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidKeynoteVerticalModel* model = context;
|
||||
|
||||
// Header
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->");
|
||||
|
||||
canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit");
|
||||
|
||||
// Up
|
||||
canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);
|
||||
if(model->up_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
canvas_draw_icon(canvas, 21, 45, &I_Button_18x18);
|
||||
if(model->down_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Left
|
||||
canvas_draw_icon(canvas, 0, 35, &I_Button_18x18);
|
||||
if(model->left_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 3, 37, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Right
|
||||
canvas_draw_icon(canvas, 42, 35, &I_Button_18x18);
|
||||
if(model->right_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 45, 37, 13, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Ok
|
||||
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
|
||||
if(model->ok_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
|
||||
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
canvas_draw_icon(canvas, 63, 45, &I_Space_65x18);
|
||||
if(model->back_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
|
||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
||||
}
|
||||
|
||||
static void
|
||||
hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) {
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = true;
|
||||
}
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->up_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->down_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->left_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->right_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = false;
|
||||
}
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyBack) {
|
||||
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
|
||||
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
|
||||
hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
|
||||
hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
HidKeynoteVertical* hid_keynote_vertical = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||
hid_hal_keyboard_release_all(hid_keynote_vertical->hid);
|
||||
} else {
|
||||
hid_keynote_vertical_process(hid_keynote_vertical, event);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) {
|
||||
HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical));
|
||||
hid_keynote_vertical->view = view_alloc();
|
||||
hid_keynote_vertical->hid = hid;
|
||||
view_set_context(hid_keynote_vertical->view, hid_keynote_vertical);
|
||||
view_allocate_model(
|
||||
hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel));
|
||||
view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback);
|
||||
view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback);
|
||||
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{ model->transport = hid->transport; },
|
||||
true);
|
||||
|
||||
return hid_keynote_vertical;
|
||||
}
|
||||
|
||||
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
view_free(hid_keynote_vertical->view);
|
||||
free(hid_keynote_vertical);
|
||||
}
|
||||
|
||||
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
return hid_keynote_vertical->view;
|
||||
}
|
||||
|
||||
void hid_keynote_vertical_set_connected_status(
|
||||
HidKeynoteVertical* hid_keynote_vertical,
|
||||
bool connected) {
|
||||
furi_assert(hid_keynote_vertical);
|
||||
with_view_model(
|
||||
hid_keynote_vertical->view,
|
||||
HidKeynoteVerticalModel * model,
|
||||
{ model->connected = connected; },
|
||||
true);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct Hid Hid;
|
||||
typedef struct HidKeynoteVertical HidKeynoteVertical;
|
||||
|
||||
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid);
|
||||
|
||||
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical);
|
||||
|
||||
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical);
|
||||
|
||||
void hid_keynote_vertical_set_connected_status(
|
||||
HidKeynoteVertical* hid_keynote_vertical,
|
||||
bool connected);
|
56
applications/external/hid_app/views/hid_numpad.c
vendored
@ -39,26 +39,26 @@ typedef struct {
|
||||
int8_t y;
|
||||
} HidNumpadPoint;
|
||||
|
||||
#define MARGIN_TOP 0
|
||||
#define MARGIN_LEFT 24
|
||||
#define MARGIN_TOP 32
|
||||
#define MARGIN_LEFT 1
|
||||
#define KEY_WIDTH 20
|
||||
#define KEY_HEIGHT 15
|
||||
#define KEY_PADDING 1
|
||||
#define ROW_COUNT 5
|
||||
#define COLUMN_COUNT 4
|
||||
#define ROW_COUNT 6
|
||||
#define COLUMN_COUNT 3
|
||||
|
||||
const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
||||
{
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
|
||||
// {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
|
||||
},
|
||||
{
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
|
||||
{.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
|
||||
// {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
|
||||
},
|
||||
{
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
|
||||
@ -69,13 +69,18 @@ const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
|
||||
{.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
|
||||
// {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
|
||||
},
|
||||
{
|
||||
{.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
|
||||
{.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
|
||||
},
|
||||
{
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
|
||||
{.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
|
||||
},
|
||||
};
|
||||
|
||||
static void hid_numpad_draw_key(
|
||||
@ -128,26 +133,36 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
HidNumpadModel* model = context;
|
||||
|
||||
if((!model->connected) && (model->transport == HidTransportBle)) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad");
|
||||
// Header
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
if(model->transport == HidTransportBle) {
|
||||
if(model->connected) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
|
||||
}
|
||||
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
|
||||
|
||||
canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit");
|
||||
} else {
|
||||
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
|
||||
}
|
||||
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection...");
|
||||
canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
|
||||
|
||||
if(!model->connected && (model->transport == HidTransportBle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontKeyboard);
|
||||
uint8_t initY = model->y == 0 ? 0 : 1;
|
||||
uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
|
||||
|
||||
if(model->y > 5) {
|
||||
initY = model->y - 4;
|
||||
}
|
||||
// if(model->y > ROW_COUNT) {
|
||||
// initY = model->y - (ROW_COUNT - 1);
|
||||
// }
|
||||
|
||||
for(uint8_t y = initY; y < ROW_COUNT; y++) {
|
||||
const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
|
||||
@ -269,6 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
|
||||
hid_numpad->hid = bt_hid;
|
||||
view_set_context(hid_numpad->view, hid_numpad);
|
||||
view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
|
||||
view_set_orientation(hid_numpad->view, ViewOrientationVertical);
|
||||
view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
|
||||
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
|
||||
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="ir_scope.png",
|
||||
fap_category="Tools",
|
||||
fap_author="@kallanreed",
|
||||
fap_version="1.0",
|
||||
fap_description="App allows to see incoming IR signals.",
|
||||
)
|
||||
|
@ -20,4 +20,7 @@ App(
|
||||
),
|
||||
],
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@oleksiikutuzov",
|
||||
fap_version="1.0",
|
||||
fap_description="Lightmeter app for photography based on BH1750 sensor",
|
||||
)
|
||||
|
@ -11,4 +11,7 @@ App(
|
||||
fap_icon_assets="images",
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_author="@panki27 & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Metronome app",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
fap_category="Games",
|
||||
fap_icon="minesweeper_icon.png",
|
||||
order=35,
|
||||
fap_author="@panki27 & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Minesweeper Game",
|
||||
)
|
||||
|
@ -9,6 +9,8 @@ App(
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_icon="morse_code_10px.png",
|
||||
fap_category="Media"
|
||||
|
||||
fap_category="Media",
|
||||
fap_author="@wh00hw & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Simple Morse Code parser",
|
||||
)
|
@ -11,6 +11,9 @@ App(
|
||||
order=60,
|
||||
fap_icon="mouse_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@mothball187 & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="App works with NRF24 Sniffer app to perform mousejack attacks",
|
||||
fap_icon_assets="images",
|
||||
fap_private_libs=[
|
||||
Lib(
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=19,
|
||||
fap_icon="converter_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_author="@theisolinearchip",
|
||||
fap_version="1.0",
|
||||
fap_description="A multi-unit converter written with an easy and expandable system for adding new units and conversion methods",
|
||||
)
|
||||
|
@ -7,18 +7,10 @@ App(
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
provides=["music_player_start"],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="icons/music_10px.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="icons",
|
||||
)
|
||||
|
||||
App(
|
||||
appid="music_player_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="music_player_on_system_start",
|
||||
requires=["music_player"],
|
||||
order=30,
|
||||
fap_libs=["music_worker"],
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "music_player_worker.h"
|
||||
#include <music_worker/music_worker.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
@ -35,7 +35,7 @@ typedef struct {
|
||||
ViewPort* view_port;
|
||||
Gui* gui;
|
||||
|
||||
MusicPlayerWorker* worker;
|
||||
MusicWorker* worker;
|
||||
} MusicPlayer;
|
||||
|
||||
static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1};
|
||||
@ -219,7 +219,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
static void music_player_worker_callback(
|
||||
static void music_worker_callback(
|
||||
uint8_t semitone,
|
||||
uint8_t dots,
|
||||
uint8_t duration,
|
||||
@ -251,7 +251,7 @@ static void music_player_worker_callback(
|
||||
void music_player_clear(MusicPlayer* instance) {
|
||||
memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||
memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||
music_player_worker_clear(instance->worker);
|
||||
music_worker_clear(instance->worker);
|
||||
}
|
||||
|
||||
MusicPlayer* music_player_alloc() {
|
||||
@ -264,10 +264,9 @@ MusicPlayer* music_player_alloc() {
|
||||
|
||||
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
instance->worker = music_player_worker_alloc();
|
||||
music_player_worker_set_volume(
|
||||
instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]);
|
||||
music_player_worker_set_callback(instance->worker, music_player_worker_callback, instance);
|
||||
instance->worker = music_worker_alloc();
|
||||
music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]);
|
||||
music_worker_set_callback(instance->worker, music_worker_callback, instance);
|
||||
|
||||
music_player_clear(instance);
|
||||
|
||||
@ -287,7 +286,7 @@ void music_player_free(MusicPlayer* instance) {
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(instance->view_port);
|
||||
|
||||
music_player_worker_free(instance->worker);
|
||||
music_worker_free(instance->worker);
|
||||
|
||||
furi_message_queue_free(instance->input_queue);
|
||||
|
||||
@ -325,12 +324,12 @@ int32_t music_player_app(void* p) {
|
||||
}
|
||||
}
|
||||
|
||||
if(!music_player_worker_load(music_player->worker, furi_string_get_cstr(file_path))) {
|
||||
if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) {
|
||||
FURI_LOG_E(TAG, "Unable to load file");
|
||||
break;
|
||||
}
|
||||
|
||||
music_player_worker_start(music_player->worker);
|
||||
music_worker_start(music_player->worker);
|
||||
|
||||
InputEvent input;
|
||||
while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) ==
|
||||
@ -344,11 +343,11 @@ int32_t music_player_app(void* p) {
|
||||
} else if(input.key == InputKeyUp) {
|
||||
if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)
|
||||
music_player->model->volume++;
|
||||
music_player_worker_set_volume(
|
||||
music_worker_set_volume(
|
||||
music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
|
||||
} else if(input.key == InputKeyDown) {
|
||||
if(music_player->model->volume > 0) music_player->model->volume--;
|
||||
music_player_worker_set_volume(
|
||||
music_worker_set_volume(
|
||||
music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
|
||||
}
|
||||
|
||||
@ -356,7 +355,7 @@ int32_t music_player_app(void* p) {
|
||||
view_port_update(music_player->view_port);
|
||||
}
|
||||
|
||||
music_player_worker_stop(music_player->worker);
|
||||
music_worker_stop(music_player->worker);
|
||||
if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg
|
||||
music_player_clear(music_player);
|
||||
} while(1);
|
||||
|
@ -1,48 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <storage/storage.h>
|
||||
#include "music_player_worker.h"
|
||||
|
||||
static void music_player_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
MusicPlayerWorker* music_player_worker = music_player_worker_alloc();
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
do {
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(args), NULL) == FSE_OK) {
|
||||
if(!music_player_worker_load(music_player_worker, furi_string_get_cstr(args))) {
|
||||
printf("Failed to open file %s\r\n", furi_string_get_cstr(args));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(!music_player_worker_load_rtttl_from_string(
|
||||
music_player_worker, furi_string_get_cstr(args))) {
|
||||
printf("Argument is not a file or RTTTL\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
music_player_worker_set_volume(music_player_worker, 1.0f);
|
||||
music_player_worker_start(music_player_worker);
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
music_player_worker_stop(music_player_worker);
|
||||
} while(0);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
music_player_worker_free(music_player_worker);
|
||||
}
|
||||
|
||||
void music_player_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL);
|
||||
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(music_player_cli);
|
||||
#endif
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*MusicPlayerWorkerCallback)(
|
||||
uint8_t semitone,
|
||||
uint8_t dots,
|
||||
uint8_t duration,
|
||||
float position,
|
||||
void* context);
|
||||
|
||||
typedef struct MusicPlayerWorker MusicPlayerWorker;
|
||||
|
||||
MusicPlayerWorker* music_player_worker_alloc();
|
||||
|
||||
void music_player_worker_clear(MusicPlayerWorker* instance);
|
||||
|
||||
void music_player_worker_free(MusicPlayerWorker* instance);
|
||||
|
||||
bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path);
|
||||
|
||||
bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path);
|
||||
|
||||
bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path);
|
||||
|
||||
bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string);
|
||||
|
||||
void music_player_worker_set_callback(
|
||||
MusicPlayerWorker* instance,
|
||||
MusicPlayerWorkerCallback callback,
|
||||
void* context);
|
||||
|
||||
void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume);
|
||||
|
||||
void music_player_worker_start(MusicPlayerWorker* instance);
|
||||
|
||||
void music_player_worker_stop(MusicPlayerWorker* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
13
applications/external/nfc_rfid_detector/application.fam
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="nfc_rfid_detector",
|
||||
name="NFC/RFID detector",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
targets=["f7"],
|
||||
entry_point="nfc_rfid_detector_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=50,
|
||||
fap_icon="nfc_rfid_detector_10px.png",
|
||||
fap_category="Tools",
|
||||
fap_icon_assets="images",
|
||||
)
|
7
applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//NfcRfidDetectorCustomEvent
|
||||
NfcRfidDetectorCustomEventStartId = 100,
|
||||
|
||||
} NfcRfidDetectorCustomEvent;
|
15
applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define NFC_RFID_DETECTOR_VERSION_APP "0.1"
|
||||
#define NFC_RFID_DETECTOR_DEVELOPED "SkorP"
|
||||
#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
|
||||
|
||||
typedef enum {
|
||||
NfcRfidDetectorViewVariableItemList,
|
||||
NfcRfidDetectorViewSubmenu,
|
||||
NfcRfidDetectorViewFieldPresence,
|
||||
NfcRfidDetectorViewWidget,
|
||||
} NfcRfidDetectorView;
|
BIN
applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png
vendored
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png
vendored
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png
vendored
Normal file
After Width: | Height: | Size: 168 B |
BIN
applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png
vendored
Normal file
After Width: | Height: | Size: 158 B |
BIN
applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png
vendored
Normal file
After Width: | Height: | Size: 124 B |
108
applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
#include "nfc_rfid_detector_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool nfc_rfid_detector_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void nfc_rfid_detector_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() {
|
||||
NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp));
|
||||
|
||||
// GUI
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View Dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_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, nfc_rfid_detector_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, nfc_rfid_detector_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Open Notification record
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// SubMenu
|
||||
app->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Field Presence
|
||||
app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
NfcRfidDetectorViewFieldPresence,
|
||||
nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Field Presence
|
||||
view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence);
|
||||
nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Notifications
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t nfc_rfid_detector_app(void* p) {
|
||||
UNUSED(p);
|
||||
NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc();
|
||||
|
||||
view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher);
|
||||
|
||||
nfc_rfid_detector_app_free(nfc_rfid_detector_app);
|
||||
|
||||
return 0;
|
||||
}
|
40
applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
#include "nfc_rfid_detector_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "NfcRfidDetector"
|
||||
|
||||
void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// start the field presence rfid detection
|
||||
furi_hal_rfid_field_detect_start();
|
||||
|
||||
// start the field presence nfc detection
|
||||
furi_hal_nfc_exit_sleep();
|
||||
furi_hal_nfc_field_detect_start();
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// stop the field presence rfid detection
|
||||
furi_hal_rfid_field_detect_stop();
|
||||
|
||||
// stop the field presence nfc detection
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// check if the field presence is nfc
|
||||
return furi_hal_nfc_field_is_present();
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) {
|
||||
furi_assert(app);
|
||||
|
||||
// check if the field presence is rfid
|
||||
return furi_hal_rfid_field_is_present(frequency);
|
||||
}
|
30
applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/nfc_rfid_detector_types.h"
|
||||
#include "helpers/nfc_rfid_detector_event.h"
|
||||
|
||||
#include "scenes/nfc_rfid_detector_scene.h"
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "views/nfc_rfid_detector_view_field_presence.h"
|
||||
|
||||
typedef struct NfcRfidDetectorApp NfcRfidDetectorApp;
|
||||
|
||||
struct NfcRfidDetectorApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
Submenu* submenu;
|
||||
Widget* widget;
|
||||
NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence;
|
||||
};
|
||||
|
||||
void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app);
|
||||
void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app);
|
||||
bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app);
|
||||
bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency);
|
31
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
#include "../nfc_rfid_detector_app_i.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = {
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) =
|
||||
{
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers nfc_rfid_detector_scene_handlers = {
|
||||
.on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers,
|
||||
.on_event_handlers = nfc_rfid_detector_scene_on_event_handlers,
|
||||
.on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers,
|
||||
.scene_num = NfcRfidDetectorSceneNum,
|
||||
};
|
29
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id,
|
||||
typedef enum {
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
NfcRfidDetectorSceneNum,
|
||||
} NfcRfidDetectorScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "nfc_rfid_detector_scene_config.h"
|
||||
#undef ADD_SCENE
|
69
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
#include "../nfc_rfid_detector_app_i.h"
|
||||
|
||||
void nfc_rfid_detector_scene_about_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_about_on_enter(void* context) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(temp_str, "\e#%s\n", "Information");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP);
|
||||
furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED);
|
||||
furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB);
|
||||
|
||||
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n");
|
||||
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! \e!\n",
|
||||
false);
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
2,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! NFC/RFID detector \e!\n",
|
||||
false);
|
||||
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_about_on_exit(void* context) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(app->widget);
|
||||
}
|
3
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
ADD_SCENE(nfc_rfid_detector, start, Start)
|
||||
ADD_SCENE(nfc_rfid_detector, about, About)
|
||||
ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence)
|
60
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
#include "../nfc_rfid_detector_app_i.h"
|
||||
#include "../views/nfc_rfid_detector_view_field_presence.h"
|
||||
|
||||
void nfc_rfid_detector_scene_field_presence_callback(
|
||||
NfcRfidDetectorCustomEvent event,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static const NotificationSequence notification_app_display_on = {
|
||||
|
||||
&message_display_backlight_on,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void nfc_rfid_detector_scene_field_presence_update(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
|
||||
uint32_t frequency = 0;
|
||||
bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app);
|
||||
bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency);
|
||||
|
||||
if(nfc_field || rfid_field)
|
||||
notification_message(app->notifications, ¬ification_app_display_on);
|
||||
|
||||
nfc_rfid_detector_view_field_presence_update(
|
||||
app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency);
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_field_presence_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
|
||||
// Start detection of field presence
|
||||
nfc_rfid_detector_app_field_presence_start(app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence);
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
nfc_rfid_detector_scene_field_presence_update(app);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_field_presence_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
// Stop detection of field presence
|
||||
nfc_rfid_detector_app_field_presence_stop(app);
|
||||
}
|
58
applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
#include "../nfc_rfid_detector_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexNfcRfidDetectorFieldPresence,
|
||||
SubmenuIndexNfcRfidDetectorAbout,
|
||||
} SubmenuIndex;
|
||||
|
||||
void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_start_on_enter(void* context) {
|
||||
UNUSED(context);
|
||||
NfcRfidDetectorApp* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Detect field type",
|
||||
SubmenuIndexNfcRfidDetectorFieldPresence,
|
||||
nfc_rfid_detector_scene_start_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"About",
|
||||
SubmenuIndexNfcRfidDetectorAbout,
|
||||
nfc_rfid_detector_scene_start_submenu_callback,
|
||||
app);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu);
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexNfcRfidDetectorAbout) {
|
||||
scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) {
|
||||
scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_scene_start_on_exit(void* context) {
|
||||
NfcRfidDetectorApp* app = context;
|
||||
submenu_reset(app->submenu);
|
||||
}
|
164
applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
#include "nfc_rfid_detector_view_field_presence.h"
|
||||
#include "../nfc_rfid_detector_app_i.h"
|
||||
#include <nfc_rfid_detector_icons.h>
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define FIELD_FOUND_WEIGHT 5
|
||||
|
||||
typedef enum {
|
||||
NfcRfidDetectorTypeFieldPresenceNfc,
|
||||
NfcRfidDetectorTypeFieldPresenceRfid,
|
||||
} NfcRfidDetectorTypeFieldPresence;
|
||||
|
||||
static const Icon* NfcRfidDetectorFieldPresenceIcons[] = {
|
||||
[NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30,
|
||||
[NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30,
|
||||
};
|
||||
|
||||
struct NfcRfidDetectorFieldPresence {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t nfc_field;
|
||||
uint8_t rfid_field;
|
||||
uint32_t rfid_frequency;
|
||||
} NfcRfidDetectorFieldPresenceModel;
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_update(
|
||||
NfcRfidDetectorFieldPresence* instance,
|
||||
bool nfc_field,
|
||||
bool rfid_field,
|
||||
uint32_t rfid_frequency) {
|
||||
furi_assert(instance);
|
||||
with_view_model(
|
||||
instance->view,
|
||||
NfcRfidDetectorFieldPresenceModel * model,
|
||||
{
|
||||
if(nfc_field) {
|
||||
model->nfc_field = FIELD_FOUND_WEIGHT;
|
||||
} else if(model->nfc_field) {
|
||||
model->nfc_field--;
|
||||
}
|
||||
if(rfid_field) {
|
||||
model->rfid_field = FIELD_FOUND_WEIGHT;
|
||||
model->rfid_frequency = rfid_frequency;
|
||||
} else if(model->rfid_field) {
|
||||
model->rfid_field--;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_draw(
|
||||
Canvas* canvas,
|
||||
NfcRfidDetectorFieldPresenceModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(!model->nfc_field && !model->rfid_field) {
|
||||
canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34);
|
||||
canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 56, 36, "Touch the reader");
|
||||
} else {
|
||||
if(model->nfc_field) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 21, 10, "NFC");
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
9,
|
||||
17,
|
||||
NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 9, 62, "13,56 MHz");
|
||||
}
|
||||
|
||||
if(model->rfid_field) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 76, 10, "LF RFID");
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
71,
|
||||
17,
|
||||
NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 69, 62, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorFieldPresence* instance = context;
|
||||
UNUSED(instance);
|
||||
|
||||
if(event->key == InputKeyBack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_enter(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorFieldPresence* instance = context;
|
||||
with_view_model(
|
||||
instance->view,
|
||||
NfcRfidDetectorFieldPresenceModel * model,
|
||||
{
|
||||
model->nfc_field = 0;
|
||||
model->rfid_field = 0;
|
||||
model->rfid_frequency = 0;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_exit(void* context) {
|
||||
furi_assert(context);
|
||||
NfcRfidDetectorFieldPresence* instance = context;
|
||||
UNUSED(instance);
|
||||
}
|
||||
|
||||
NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() {
|
||||
NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence));
|
||||
|
||||
// View allocation and configuration
|
||||
instance->view = view_alloc();
|
||||
|
||||
view_allocate_model(
|
||||
instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel));
|
||||
view_set_context(instance->view, instance);
|
||||
view_set_draw_callback(
|
||||
instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw);
|
||||
view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input);
|
||||
view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter);
|
||||
view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit);
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
NfcRfidDetectorFieldPresenceModel * model,
|
||||
{
|
||||
model->nfc_field = 0;
|
||||
model->rfid_field = 0;
|
||||
model->rfid_frequency = 0;
|
||||
},
|
||||
true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
19
applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/nfc_rfid_detector_types.h"
|
||||
#include "../helpers/nfc_rfid_detector_event.h"
|
||||
|
||||
typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence;
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_update(
|
||||
NfcRfidDetectorFieldPresence* instance,
|
||||
bool nfc_field,
|
||||
bool rfid_field,
|
||||
uint32_t rfid_frequency);
|
||||
|
||||
NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc();
|
||||
|
||||
void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance);
|
||||
|
||||
View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance);
|
@ -8,6 +8,9 @@ App(
|
||||
order=70,
|
||||
fap_icon="nrfsniff_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@mothball187 & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="App captures addresses to use with NRF24 Mouse Jacker app to perform mousejack attacks",
|
||||
fap_private_libs=[
|
||||
Lib(
|
||||
name="nrf24",
|
||||
|
@ -9,4 +9,7 @@ App(
|
||||
fap_icon="playlist_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
fap_author="@darmiel",
|
||||
fap_version="1.0",
|
||||
fap_description="App works with list of sub-ghz files from .txt file that contains paths to target files.",
|
||||
)
|
||||
|
@ -9,4 +9,7 @@ App(
|
||||
fap_icon="pocsag_pager_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
fap_author="@xMasterX & @Shmuma",
|
||||
fap_version="1.0",
|
||||
fap_description="App can capture POCSAG 1200 messages on CC1101 supported frequencies.",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=50,
|
||||
fap_icon="appicon.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_author="@antirez & (fixes by @xMasterX)",
|
||||
fap_version="1.0",
|
||||
fap_description="Digital signal detection, visualization, editing and reply tool",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=80,
|
||||
fap_icon="safe_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@H4ckd4ddy & @xMasterX (ported to latest firmware)",
|
||||
fap_version="1.0",
|
||||
fap_description="App exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code via UART pins.",
|
||||
)
|
||||
|
@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) {
|
||||
signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app);
|
||||
|
||||
signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY);
|
||||
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
|
||||
|
||||
if(!furi_hal_pwm_is_running(pwm_ch_id[0])) {
|
||||
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
|
||||
} else {
|
||||
furi_hal_pwm_stop(pwm_ch_id[0]);
|
||||
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
|
||||
}
|
||||
}
|
||||
|
||||
bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
|
||||
@ -46,8 +52,18 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
|
||||
furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty);
|
||||
} else if(event.event == SignalGenPwmEventChannelChange) {
|
||||
consumed = true;
|
||||
furi_hal_pwm_stop(app->pwm_ch_prev);
|
||||
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
|
||||
// Stop previous channel PWM
|
||||
if(furi_hal_pwm_is_running(app->pwm_ch_prev)) {
|
||||
furi_hal_pwm_stop(app->pwm_ch_prev);
|
||||
}
|
||||
|
||||
// Start PWM and restart if it was starter already
|
||||
if(furi_hal_pwm_is_running(app->pwm_ch)) {
|
||||
furi_hal_pwm_stop(app->pwm_ch);
|
||||
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
|
||||
} else {
|
||||
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
|
||||
}
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
@ -56,5 +72,8 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
|
||||
void signal_gen_scene_pwm_on_exit(void* context) {
|
||||
SignalGenApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
furi_hal_pwm_stop(app->pwm_ch);
|
||||
|
||||
if(furi_hal_pwm_is_running(app->pwm_ch)) {
|
||||
furi_hal_pwm_stop(app->pwm_ch);
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,8 @@ App(
|
||||
order=30,
|
||||
fap_icon="solitaire_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets"
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@teeebor",
|
||||
fap_version="1.0",
|
||||
fap_description="Solitaire game",
|
||||
)
|
@ -8,4 +8,7 @@ App(
|
||||
order=12,
|
||||
fap_icon="spectrum_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_author="@xMasterX & @theY4Kman (original by @jolcese)",
|
||||
fap_version="1.0",
|
||||
fap_description="Shows received signals on spectrum, not actual analyzer, more like a demo app",
|
||||
)
|
||||
|
@ -8,5 +8,8 @@ App(
|
||||
order=10,
|
||||
fap_icon="icons/app.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="icons"
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@g3gg0 & (fixes by @xMasterX)",
|
||||
fap_version="1.0",
|
||||
fap_description="ARM SWD (Single Wire Debug) Probe",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=20,
|
||||
fap_icon="tetris_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="@xMasterX & @jeffplang",
|
||||
fap_version="1.0",
|
||||
fap_description="Tetris Game",
|
||||
)
|
||||
|
@ -12,4 +12,7 @@ App(
|
||||
fap_icon="icons/text_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@kowalski7cc & @kyhwana",
|
||||
fap_version="1.0",
|
||||
fap_description="Text viewer application",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=40,
|
||||
fap_icon="tictactoe_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="@xMasterX & @gotnull",
|
||||
fap_version="1.0",
|
||||
fap_description="Tic Tac Toe game, for 2 players, play on one device",
|
||||
)
|
||||
|
@ -52,10 +52,10 @@ void totp_type_code_worker_execute_automation(
|
||||
while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) {
|
||||
uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char);
|
||||
if(char_index > 9) {
|
||||
char_index = cb_char - 0x41 + 10;
|
||||
char_index = cb_char - 'A' + 10;
|
||||
}
|
||||
|
||||
if(char_index > 35) break;
|
||||
if(char_index >= sizeof(hid_number_keys)) break;
|
||||
|
||||
uint16_t hid_kb_key = hid_number_keys[char_index];
|
||||
if(char_index > 9) {
|
||||
|
@ -9,4 +9,7 @@ App(
|
||||
fap_icon="uart_terminal.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@cool4uma & (some fixes by @xMasterX)",
|
||||
fap_version="1.0",
|
||||
fap_description="App to control various devices via UART interface.",
|
||||
)
|
||||
|
15
applications/external/unitemp/Sensors.c
vendored
@ -79,7 +79,7 @@ static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT
|
||||
&Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10,
|
||||
&SHT30, &GXHT30, &LM75, &HDC1080, &BMP180,
|
||||
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675,
|
||||
&SCD30};
|
||||
&SCD30, &SCD40};
|
||||
|
||||
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
|
||||
if(index > SENSOR_TYPES_COUNT) return NULL;
|
||||
@ -624,11 +624,16 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) {
|
||||
UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status);
|
||||
}
|
||||
|
||||
if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) {
|
||||
uintemp_celsiumToFarengate(sensor);
|
||||
}
|
||||
|
||||
if(sensor->status == UT_SENSORSTATUS_OK) {
|
||||
if(app->settings.heat_index &&
|
||||
((sensor->type->datatype & (UT_TEMPERATURE | UT_HUMIDITY)) ==
|
||||
(UT_TEMPERATURE | UT_HUMIDITY))) {
|
||||
unitemp_calculate_heat_index(sensor);
|
||||
}
|
||||
if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT) {
|
||||
uintemp_celsiumToFarengate(sensor);
|
||||
}
|
||||
|
||||
sensor->temp += sensor->temp_offset / 10.f;
|
||||
if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) {
|
||||
unitemp_pascalToMmHg(sensor);
|
||||
|
2
applications/external/unitemp/Sensors.h
vendored
@ -119,6 +119,7 @@ typedef struct Sensor {
|
||||
char* name;
|
||||
//Температура
|
||||
float temp;
|
||||
float heat_index;
|
||||
//Относительная влажность
|
||||
float hum;
|
||||
//Атмосферное давление
|
||||
@ -334,4 +335,5 @@ const GPIO*
|
||||
#include "./sensors/MAX31855.h"
|
||||
#include "./sensors/MAX6675.h"
|
||||
#include "./sensors/SCD30.h"
|
||||
#include "./sensors/SCD40.h"
|
||||
#endif
|
||||
|
@ -9,8 +9,9 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
order=100,
|
||||
fap_description = "Universal temperature sensors reader",
|
||||
fap_author = "Quenon",
|
||||
fap_weburl = "https://github.com/quen0n/Unitemp-Flipper-Zero-Plugin",
|
||||
fap_version="1.4",
|
||||
fap_author = "@quen0n & (fixes by @xMasterX)",
|
||||
fap_weburl = "https://github.com/quen0n/unitemp-flipperzero",
|
||||
fap_category="GPIO",
|
||||
fap_icon="icon.png",
|
||||
fap_icon_assets="assets",
|
||||
|
BIN
applications/external/unitemp/assets/heat_index_11x14.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
60
applications/external/unitemp/interfaces/endianness.h
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// Created by Avilov Vasily on 10.06.2023.
|
||||
//
|
||||
|
||||
#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
||||
#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
||||
|
||||
inline static void store16(uint8_t* b, uint16_t i) {
|
||||
memcpy(b, &i, 2);
|
||||
}
|
||||
|
||||
inline static void store32(uint8_t* b, uint32_t i) {
|
||||
memcpy(b, &i, 4);
|
||||
}
|
||||
|
||||
inline static uint16_t load16(uint8_t* b) {
|
||||
uint16_t x;
|
||||
memcpy(&x, b, 2);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static uint32_t load32(uint8_t* b) {
|
||||
uint32_t x;
|
||||
memcpy(&x, b, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define htobe16(x) (x)
|
||||
#define htobe32(x) (x)
|
||||
#define htole16(x) __builtin_bswap16(x)
|
||||
#define htole32(x) __builtin_bswap32(x)
|
||||
#define be16toh(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define le16toh(x) __builtin_bswap16(x)
|
||||
#define le32toh(x) __builtin_bswap32(x)
|
||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#else
|
||||
#error "What kind of system is this?"
|
||||
#endif
|
||||
|
||||
#define load16_le(b) (le16toh(load16(b)))
|
||||
#define load32_le(b) (le32toh(load32(b)))
|
||||
#define store16_le(b, i) (store16(b, htole16(i)))
|
||||
#define store32_le(b, i) (store32(b, htole32(i)))
|
||||
|
||||
#define load16_be(b) (be16toh(load16(b)))
|
||||
#define load32_be(b) (be32toh(load32(b)))
|
||||
#define store16_be(b, i) (store16(b, htobe16(i)))
|
||||
#define store32_be(b, i) (store32(b, htobe32(i)))
|
||||
|
||||
#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
53
applications/external/unitemp/sensors/SCD30.c
vendored
@ -21,60 +21,9 @@
|
||||
|
||||
#include "SCD30.h"
|
||||
#include "../interfaces/I2CSensor.h"
|
||||
#include "../interfaces/endianness.h"
|
||||
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
|
||||
|
||||
inline static uint16_t load16(uint8_t* b) {
|
||||
uint16_t x;
|
||||
memcpy(&x, b, 2);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static uint32_t load32(uint8_t* b) {
|
||||
uint32_t x;
|
||||
memcpy(&x, b, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static void store16(uint8_t* b, uint16_t i) {
|
||||
memcpy(b, &i, 2);
|
||||
}
|
||||
|
||||
inline static void store32(uint8_t* b, uint32_t i) {
|
||||
memcpy(b, &i, 4);
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define htobe16(x) (x)
|
||||
#define htobe32(x) (x)
|
||||
#define htole16(x) __builtin_bswap16(x)
|
||||
#define htole32(x) __builtin_bswap32(x)
|
||||
#define be16toh(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define le16toh(x) __builtin_bswap16(x)
|
||||
#define le32toh(x) __builtin_bswap32(x)
|
||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#else
|
||||
#error "What kind of system is this?"
|
||||
#endif
|
||||
|
||||
#define load16_le(b) (le16toh(load16(b)))
|
||||
#define load32_le(b) (le32toh(load32(b)))
|
||||
#define store16_le(b, i) (store16(b, htole16(i)))
|
||||
#define store32_le(b, i) (store32(b, htole32(i)))
|
||||
|
||||
#define load16_be(b) (be16toh(load16(b)))
|
||||
#define load32_be(b) (be32toh(load32(b)))
|
||||
#define store16_be(b, i) (store16(b, htobe16(i)))
|
||||
#define store32_be(b, i) (store32(b, htobe32(i)))
|
||||
|
||||
typedef union {
|
||||
uint16_t array16[2];
|
||||
uint8_t array8[4];
|
||||
|
291
applications/external/unitemp/sensors/SCD40.c
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
Unitemp - Universal temperature reader
|
||||
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
|
||||
Contributed by divinebird (https://github.com/divinebird)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
|
||||
|
||||
#include "SCD30.h"
|
||||
#include "../interfaces/I2CSensor.h"
|
||||
#include "../interfaces/endianness.h"
|
||||
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
|
||||
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
|
||||
bool unitemp_SCD40_init(Sensor* sensor);
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor);
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
|
||||
bool unitemp_SCD40_free(Sensor* sensor);
|
||||
|
||||
const SensorType SCD40 = {
|
||||
.typename = "SCD40",
|
||||
.interface = &I2C,
|
||||
.datatype = UT_DATA_TYPE_TEMP_HUM_CO2,
|
||||
.pollingInterval = 5000,
|
||||
.allocator = unitemp_SCD40_alloc,
|
||||
.mem_releaser = unitemp_SCD40_free,
|
||||
.initializer = unitemp_SCD40_init,
|
||||
.deinitializer = unitemp_SCD40_deinit,
|
||||
.updater = unitemp_SCD40_update};
|
||||
|
||||
#define SCD40_ID 0x62
|
||||
|
||||
#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1
|
||||
#define COMMAND_READ_MEASUREMENT 0XEC05
|
||||
#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86
|
||||
|
||||
#define COMMAND_PERSIST_SETTINGS 0X3615
|
||||
#define COMMAND_GET_SERIAL_NUMBER 0X3682
|
||||
#define COMMAND_PERFORM_SELF_TEST 0X3639
|
||||
#define COMMAND_PERFORM_FACTORY_RESET 0X3632
|
||||
#define COMMAND_REINIT 0X3646
|
||||
|
||||
#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D
|
||||
#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318
|
||||
#define COMMAND_SET_SENSOR_ALTITUDE 0X2427
|
||||
#define COMMAND_GET_SENSOR_ALTITUDE 0X2322
|
||||
#define COMMAND_SET_AMBIENT_PRESSURE 0XE000
|
||||
#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F
|
||||
#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416
|
||||
#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313
|
||||
|
||||
static bool readMeasurement(Sensor* sensor) __attribute__((unused));
|
||||
static void reset(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused));
|
||||
static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused));
|
||||
|
||||
static float getTemperatureOffset(Sensor* sensor) __attribute__((unused));
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused));
|
||||
|
||||
static bool beginMeasuring(Sensor* sensor) __attribute__((unused));
|
||||
static bool stopMeasurement(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args) {
|
||||
UNUSED(args);
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
|
||||
i2c_sensor->minI2CAdr = SCD40_ID << 1;
|
||||
i2c_sensor->maxI2CAdr = SCD40_ID << 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_free(Sensor* sensor) {
|
||||
//Нечего высвобождать, так как ничего не было выделено
|
||||
UNUSED(sensor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_init(Sensor* sensor) {
|
||||
return beginMeasuring(sensor);
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor) {
|
||||
return stopMeasurement(sensor);
|
||||
}
|
||||
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor) {
|
||||
readMeasurement(sensor);
|
||||
return UT_SENSORSTATUS_OK;
|
||||
}
|
||||
|
||||
#define CRC8_POLYNOMIAL 0x31
|
||||
#define CRC8_INIT 0xFF
|
||||
|
||||
static uint8_t computeCRC8(uint8_t* message, uint8_t len) {
|
||||
uint8_t crc = CRC8_INIT; // Init with 0xFF
|
||||
for(uint8_t x = 0; x < len; x++) {
|
||||
crc ^= message[x]; // XOR-in the next input byte
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if((crc & 0x80) != 0)
|
||||
crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc; // No output reflection
|
||||
}
|
||||
|
||||
// Sends a command along with arguments and CRC
|
||||
static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) {
|
||||
static const uint8_t cmdSize = 5;
|
||||
|
||||
uint8_t bytes[cmdSize];
|
||||
uint8_t* pointer = bytes;
|
||||
store16_be(pointer, command);
|
||||
pointer += 2;
|
||||
uint8_t* argPos = pointer;
|
||||
store16_be(pointer, arguments);
|
||||
pointer += 2;
|
||||
*pointer = computeCRC8(argPos, pointer - argPos);
|
||||
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
|
||||
}
|
||||
|
||||
// Sends just a command, no arguments, no CRC
|
||||
static bool sendCommand(Sensor* sensor, uint16_t command) {
|
||||
static const uint8_t cmdSize = 2;
|
||||
|
||||
uint8_t bytes[cmdSize];
|
||||
store16_be(bytes, command);
|
||||
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
|
||||
}
|
||||
|
||||
static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) {
|
||||
static const uint8_t regSize = 2;
|
||||
|
||||
if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
uint8_t bytes[regSize];
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0;
|
||||
|
||||
return load16_be(bytes);
|
||||
}
|
||||
|
||||
static bool loadWord(uint8_t* buff, uint16_t* val) {
|
||||
uint16_t tmp = load16_be(buff);
|
||||
uint8_t expectedCRC = computeCRC8(buff, 2);
|
||||
if(buff[2] != expectedCRC) return false;
|
||||
*val = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) {
|
||||
static const uint8_t respSize = 3;
|
||||
|
||||
if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
uint8_t bytes[respSize];
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false;
|
||||
|
||||
return loadWord(bytes, val);
|
||||
}
|
||||
|
||||
// Get 18 bytes from SCD30
|
||||
// Updates global variables with floats
|
||||
// Returns true if success
|
||||
static bool readMeasurement(Sensor* sensor) {
|
||||
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
|
||||
return false; // Sensor did not ACK
|
||||
}
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
static const uint8_t respSize = 9;
|
||||
uint8_t buff[respSize];
|
||||
uint8_t* bytes = buff;
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
|
||||
FURI_LOG_E(APP_NAME, "Error while read measures");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t tmpValue;
|
||||
|
||||
bool error = false;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->co2 = tmpValue;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing CO2");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 3;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing temp");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 3;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->hum = (float)tmpValue * 100.0f / 65535.0f;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing humidity");
|
||||
error = true;
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
static void reset(Sensor* sensor) {
|
||||
sendCommand(sensor, COMMAND_REINIT);
|
||||
}
|
||||
|
||||
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) {
|
||||
return sendCommandWithCRC(
|
||||
sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC
|
||||
}
|
||||
|
||||
// Get the current ASC setting
|
||||
static bool getAutoSelfCalibration(Sensor* sensor) {
|
||||
return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED);
|
||||
}
|
||||
|
||||
// Unfinished
|
||||
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) {
|
||||
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
|
||||
return false; // Sensor did not ACK
|
||||
}
|
||||
|
||||
static const uint8_t respSize = 9;
|
||||
uint8_t buff[respSize];
|
||||
uint8_t* bytes = buff;
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
|
||||
FURI_LOG_E(APP_NAME, "Error while read measures");
|
||||
return false;
|
||||
}
|
||||
|
||||
*val = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool beginMeasuring(Sensor* sensor) {
|
||||
return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT);
|
||||
}
|
||||
|
||||
// Stop continuous measurement
|
||||
static bool stopMeasurement(Sensor* sensor) {
|
||||
return sendCommand(sensor, COMMAND_READ_MEASUREMENT);
|
||||
}
|
||||
|
||||
static float getTemperatureOffset(Sensor* sensor) {
|
||||
uint16_t curOffset;
|
||||
if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0;
|
||||
return (float)curOffset * 175.0f / 65536.0f;
|
||||
}
|
||||
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) {
|
||||
uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f;
|
||||
return sendCommandWithCRC(
|
||||
sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC
|
||||
}
|
59
applications/external/unitemp/sensors/SCD40.h
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Unitemp - Universal temperature reader
|
||||
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
|
||||
Contributed by divinebird (https://github.com/divinebird)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef UNITEMP_SCD40
|
||||
#define UNITEMP_SCD40
|
||||
|
||||
#include "../unitemp.h"
|
||||
#include "../Sensors.h"
|
||||
|
||||
extern const SensorType SCD40;
|
||||
/**
|
||||
* @brief Выделение памяти и установка начальных значений датчика SCD40
|
||||
* @param sensor Указатель на создаваемый датчик
|
||||
* @return Истина при успехе
|
||||
*/
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Инициализации датчика SCD40
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Истина если инициализация упспешная
|
||||
*/
|
||||
bool unitemp_SCD40_init(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Обновление значений из датчика
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус опроса датчика
|
||||
*/
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Высвободить память датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD40_free(Sensor* sensor);
|
||||
|
||||
#endif
|
30
applications/external/unitemp/unitemp.c
vendored
@ -28,8 +28,31 @@ Unitemp* app;
|
||||
|
||||
void uintemp_celsiumToFarengate(Sensor* sensor) {
|
||||
sensor->temp = sensor->temp * (9.0 / 5.0) + 32;
|
||||
sensor->heat_index = sensor->heat_index * (9.0 / 5.0) + 32;
|
||||
}
|
||||
|
||||
static float heat_index_consts[9] = {
|
||||
-42.379f,
|
||||
2.04901523f,
|
||||
10.14333127f,
|
||||
-0.22475541f,
|
||||
-0.00683783f,
|
||||
-0.05481717f,
|
||||
0.00122874f,
|
||||
0.00085282f,
|
||||
-0.00000199f};
|
||||
void unitemp_calculate_heat_index(Sensor* sensor) {
|
||||
// temp should be in Celsius, heat index will be in Celsius
|
||||
float temp = sensor->temp * (9.0 / 5.0) + 32.0f;
|
||||
float hum = sensor->hum;
|
||||
sensor->heat_index =
|
||||
(heat_index_consts[0] + heat_index_consts[1] * temp + heat_index_consts[2] * hum +
|
||||
heat_index_consts[3] * temp * hum + heat_index_consts[4] * temp * temp +
|
||||
heat_index_consts[5] * hum * hum + heat_index_consts[6] * temp * temp * hum +
|
||||
heat_index_consts[7] * temp * hum * hum + heat_index_consts[8] * temp * temp * hum * hum -
|
||||
32.0f) *
|
||||
(5.0 / 9.0);
|
||||
}
|
||||
void unitemp_pascalToMmHg(Sensor* sensor) {
|
||||
sensor->pressure = sensor->pressure * 0.007500638;
|
||||
}
|
||||
@ -71,6 +94,7 @@ bool unitemp_saveSettings(void) {
|
||||
app->file_stream, "INFINITY_BACKLIGHT %d\n", app->settings.infinityBacklight);
|
||||
stream_write_format(app->file_stream, "TEMP_UNIT %d\n", app->settings.temp_unit);
|
||||
stream_write_format(app->file_stream, "PRESSURE_UNIT %d\n", app->settings.pressure_unit);
|
||||
stream_write_format(app->file_stream, "HEAT_INDEX %d\n", app->settings.heat_index);
|
||||
|
||||
//Закрытие потока и освобождение памяти
|
||||
file_stream_close(app->file_stream);
|
||||
@ -166,6 +190,11 @@ bool unitemp_loadSettings(void) {
|
||||
int p = 0;
|
||||
sscanf(((char*)(file_buf + line_end)), "\nPRESSURE_UNIT %d", &p);
|
||||
app->settings.pressure_unit = p;
|
||||
} else if(!strcmp(buff, "HEAT_INDEX")) {
|
||||
//Чтение значения параметра
|
||||
int p = 0;
|
||||
sscanf(((char*)(file_buf + line_end)), "\nHEAT_INDEX %d", &p);
|
||||
app->settings.heat_index = p;
|
||||
} else {
|
||||
FURI_LOG_W(APP_NAME, "Unknown settings parameter: %s", buff);
|
||||
}
|
||||
@ -203,6 +232,7 @@ static bool unitemp_alloc(void) {
|
||||
app->settings.infinityBacklight = true; //Подсветка горит всегда
|
||||
app->settings.temp_unit = UT_TEMP_CELSIUS; //Единица измерения температуры - градусы Цельсия
|
||||
app->settings.pressure_unit = UT_PRESSURE_MM_HG; //Единица измерения давления - мм рт. ст.
|
||||
app->settings.heat_index = false;
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
//Диспетчер окон
|
||||
|
11
applications/external/unitemp/unitemp.h
vendored
@ -40,7 +40,7 @@
|
||||
//Имя приложения
|
||||
#define APP_NAME "Unitemp"
|
||||
//Версия приложения
|
||||
#define UNITEMP_APP_VER "1.3"
|
||||
#define UNITEMP_APP_VER "1.4"
|
||||
//Путь хранения файлов плагина
|
||||
#define APP_PATH_FOLDER "/ext/unitemp"
|
||||
//Имя файла с настройками
|
||||
@ -80,6 +80,8 @@ typedef struct {
|
||||
tempMeasureUnit temp_unit;
|
||||
//Единица измерения давления
|
||||
pressureMeasureUnit pressure_unit;
|
||||
// Do calculate and show heat index
|
||||
bool heat_index;
|
||||
//Последнее состояние OTG
|
||||
bool lastOTGState;
|
||||
} UnitempSettings;
|
||||
@ -111,6 +113,13 @@ typedef struct {
|
||||
|
||||
/* Объявление прототипов функций */
|
||||
|
||||
/**
|
||||
* @brief Calculates the heat index in Celsius from the temperature and humidity and stores it in the sensor heat_index field
|
||||
*
|
||||
* @param sensor The sensor struct, with temperature in Celcius and humidity in percent
|
||||
*/
|
||||
void unitemp_calculate_heat_index(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Перевод значения температуры датчика из Цельсия в Фаренгейты
|
||||
*
|
||||
|
@ -113,6 +113,33 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2])
|
||||
canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%");
|
||||
}
|
||||
|
||||
static void _draw_heat_index(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) {
|
||||
canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3);
|
||||
canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3);
|
||||
|
||||
canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 3, &I_heat_index_11x14);
|
||||
|
||||
int16_t heat_index_int = sensor->heat_index;
|
||||
int8_t heat_index_dec = abs((int16_t)(sensor->heat_index * 10) % 10);
|
||||
|
||||
snprintf(app->buff, BUFF_SIZE, "%d", heat_index_int);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
pos[0] + 27 + ((sensor->heat_index <= -10 || sensor->heat_index > 99) ? 5 : 0),
|
||||
pos[1] + 10,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
app->buff);
|
||||
|
||||
if(heat_index_int <= 99) {
|
||||
uint8_t int_len = canvas_string_width(canvas, app->buff);
|
||||
snprintf(app->buff, BUFF_SIZE, ".%d", heat_index_dec);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 2, pos[1] + 10 + 7, app->buff);
|
||||
}
|
||||
}
|
||||
|
||||
static void _draw_pressure(Canvas* canvas, Sensor* sensor) {
|
||||
const uint8_t x = 29, y = 39;
|
||||
//Рисование рамки
|
||||
@ -320,12 +347,23 @@ static void _draw_carousel_values(Canvas* canvas) {
|
||||
ColorWhite);
|
||||
break;
|
||||
case UT_DATA_TYPE_TEMP_HUM:
|
||||
_draw_temperature(
|
||||
canvas,
|
||||
unitemp_sensor_getActive(generalview_sensor_index),
|
||||
temp_positions[1][0],
|
||||
temp_positions[1][1],
|
||||
ColorWhite);
|
||||
if(!app->settings.heat_index) {
|
||||
_draw_temperature(
|
||||
canvas,
|
||||
unitemp_sensor_getActive(generalview_sensor_index),
|
||||
temp_positions[1][0],
|
||||
temp_positions[1][1],
|
||||
ColorWhite);
|
||||
} else {
|
||||
_draw_temperature(
|
||||
canvas,
|
||||
unitemp_sensor_getActive(generalview_sensor_index),
|
||||
temp_positions[2][0],
|
||||
temp_positions[2][1],
|
||||
ColorWhite);
|
||||
_draw_heat_index(
|
||||
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]);
|
||||
}
|
||||
_draw_humidity(
|
||||
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[0]);
|
||||
break;
|
||||
@ -446,8 +484,8 @@ static void _draw_carousel_info(Canvas* canvas) {
|
||||
->currentI2CAdr >>
|
||||
1);
|
||||
canvas_draw_str(canvas, 57, 35, app->buff);
|
||||
canvas_draw_str(canvas, 54, 46, "15 (C0)");
|
||||
canvas_draw_str(canvas, 54, 58, "16 (C1)");
|
||||
canvas_draw_str(canvas, 54, 46, "15 (C1)");
|
||||
canvas_draw_str(canvas, 54, 58, "16 (C0)");
|
||||
}
|
||||
}
|
||||
static void _draw_view_sensorsCarousel(Canvas* canvas) {
|
||||
|
@ -26,6 +26,7 @@ static VariableItemList* variable_item_list;
|
||||
static const char states[2][9] = {"Auto", "Infinity"};
|
||||
static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"};
|
||||
static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"};
|
||||
static const char heat_index_bool[2][4] = {"OFF", "ON"};
|
||||
|
||||
//Элемент списка - бесконечная подсветка
|
||||
VariableItem* infinity_backlight_item;
|
||||
@ -33,6 +34,8 @@ VariableItem* infinity_backlight_item;
|
||||
VariableItem* temperature_unit_item;
|
||||
//Единица измерения давления
|
||||
VariableItem* pressure_unit_item;
|
||||
|
||||
VariableItem* heat_index_item;
|
||||
#define VIEW_ID UnitempViewSettings
|
||||
|
||||
/**
|
||||
@ -57,6 +60,7 @@ static uint32_t _exit_callback(void* context) {
|
||||
(bool)variable_item_get_current_value_index(infinity_backlight_item);
|
||||
app->settings.temp_unit = variable_item_get_current_value_index(temperature_unit_item);
|
||||
app->settings.pressure_unit = variable_item_get_current_value_index(pressure_unit_item);
|
||||
app->settings.heat_index = variable_item_get_current_value_index(heat_index_item);
|
||||
unitemp_saveSettings();
|
||||
unitemp_loadSettings();
|
||||
|
||||
@ -90,6 +94,11 @@ static void _setting_change_callback(VariableItem* item) {
|
||||
pressure_unit_item,
|
||||
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
|
||||
}
|
||||
if(item == heat_index_item) {
|
||||
variable_item_set_current_value_text(
|
||||
heat_index_item,
|
||||
heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,6 +115,8 @@ void unitemp_Settings_alloc(void) {
|
||||
variable_item_list_add(variable_item_list, "Temp. unit", 2, _setting_change_callback, app);
|
||||
pressure_unit_item = variable_item_list_add(
|
||||
variable_item_list, "Press. unit", UT_PRESSURE_COUNT, _setting_change_callback, app);
|
||||
heat_index_item = variable_item_list_add(
|
||||
variable_item_list, "Calc. heat index", 2, _setting_change_callback, app);
|
||||
|
||||
//Добавление колбека на нажатие средней кнопки
|
||||
variable_item_list_set_enter_callback(variable_item_list, _enter_callback, app);
|
||||
@ -139,6 +150,10 @@ void unitemp_Settings_switch(void) {
|
||||
pressure_unit_item,
|
||||
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
|
||||
|
||||
variable_item_set_current_value_index(heat_index_item, (uint8_t)app->settings.heat_index);
|
||||
variable_item_set_current_value_text(
|
||||
heat_index_item, heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID);
|
||||
}
|
||||
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
fap_icon="wav_10px.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="images",
|
||||
fap_author="@DrZlo13 & (ported, fixed by @xMasterX), (improved by @LTVA1)",
|
||||
fap_version="1.0",
|
||||
fap_description="Audio player for WAV files, recommended to convert files to unsigned 8-bit PCM stereo, but it may work with others too",
|
||||
)
|
||||
|
@ -106,6 +106,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) {
|
||||
furi_thread_free(uart->rx_thread);
|
||||
|
||||
furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL);
|
||||
if(uart->channel == FuriHalUartIdLPUART1) {
|
||||
furi_hal_uart_deinit(uart->channel);
|
||||
}
|
||||
furi_hal_console_enable();
|
||||
|
||||
free(uart);
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=110,
|
||||
fap_icon="wifi_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@SequoiaSan & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="WiFi scanner module interface, based on ESP8266",
|
||||
)
|
||||
|
@ -8,4 +8,7 @@ App(
|
||||
order=280,
|
||||
fap_icon="zombie_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="@DevMilanIan & @xMasterX, (original By @Dooskington)",
|
||||
fap_version="1.0",
|
||||
fap_description="Defend your walls from the zombies",
|
||||
)
|
||||
|