Merge branch 'dev' into subghz/code_improvement

This commit is contained in:
MX 2023-07-02 11:55:38 +03:00
commit a8dd326fdc
No known key found for this signature in database
GPG Key ID: 7CCC66B7DBDD1C83
197 changed files with 5675 additions and 2129 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -12,5 +12,6 @@ App(
"display_test",
"text_box_test",
"file_browser_test",
"speaker_debug",
],
)

View 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",
)

View 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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View 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"],
)

View 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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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() {

View File

@ -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",
)

View File

@ -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) {

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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.",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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.",
)

View File

@ -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",
)

View File

@ -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)",
)

View File

@ -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.",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -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);

View File

@ -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;

View File

@ -1,7 +1,6 @@
typedef enum {
HidViewSubmenu,
HidViewKeynote,
HidViewKeynoteVertical,
HidViewKeyboard,
HidViewNumpad,
HidViewMedia,

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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.",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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(

View File

@ -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",
)

View File

@ -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"],
)

View File

@ -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);

View File

@ -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
}

View File

@ -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

View 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",
)

View File

@ -0,0 +1,7 @@
#pragma once
typedef enum {
//NfcRfidDetectorCustomEvent
NfcRfidDetectorCustomEventStartId = 100,
} NfcRfidDetectorCustomEvent;

View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

View 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;
}

View 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);
}

View 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);

View 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,
};

View 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

View 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);
}

View 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)

View 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, &notification_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);
}

View 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);
}

View 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;
}

View 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);

View File

@ -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",

View File

@ -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.",
)

View File

@ -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.",
)

View File

@ -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",
)

View File

@ -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.",
)

View File

@ -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);
}
}

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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",
)

View File

@ -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) {

View File

@ -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.",
)

View File

@ -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);

View File

@ -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

View File

@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View 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

View File

@ -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];

View 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
}

View 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

View File

@ -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);
//Диспетчер окон

View File

@ -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 Перевод значения температуры датчика из Цельсия в Фаренгейты
*

View File

@ -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) {

View File

@ -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);
}

View File

@ -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",
)

View File

@ -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);

View File

@ -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",
)

View File

@ -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",
)

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