1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Compare commits

...

104 Commits

Author SHA1 Message Date
MX
107d574c02 added rfid fuzzer example to sdcard.zip 2022-09-05 22:28:41 +03:00
MX
84204a3233 return some plugins back 2022-09-05 22:10:31 +03:00
MX
48691b2296 Merge branch 'fz-dev' into dev 2022-09-05 20:53:59 +03:00
あく
3a767c9c02 [FL-2794] Lib: update LFS to v2.5.0, lower update free page limit (#1706)
* Lib: update lfs to v2.5.0

* Storage: set minimum free pages for update on internal storage to 3

* Updater: lower min int free space limit

* lfs: disabled debug and trace logs by default

Co-authored-by: hedger <hedger@nanode.su>
2022-09-05 20:40:20 +03:00
MX
1a4d928e5c fix rfid fuzzer gui 2022-09-05 20:01:26 +03:00
MX
a8858e38b5 Merge branch 'fz-dev' into dev 2022-09-05 19:23:34 +03:00
MX
120b5d7c90 update changelog and readme 2022-09-05 19:00:33 +03:00
Max Andreev
b7a6d18186 Fix CI/CD in tags #1703 2022-09-05 22:40:54 +09:00
VVX7
8d8481b17f fix buffer overflow in mifar ul load (#1697)
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2022-09-05 20:42:59 +09:00
MX
ca8217b692 some fixes for rfid fuzzer 2022-09-05 14:05:43 +03:00
MX
361895c689 Merge pull request #55 from Ganapati/dev
Fix memory issues and add new attack
2022-09-05 13:20:45 +03:00
Ganapati
789230458b Increase time between card to handle slow readers 2022-09-05 09:39:11 +02:00
Ganapati
6f77f537e3 Fi memory issues ad add new attack 2022-09-04 23:32:58 +02:00
MX
b6254227b9 debug pack 2022-09-03 23:47:15 +03:00
MX
8323877120 formatted, attempt to fix rfid fuzzer crash
crash doesn’t fixed with this commit
2022-09-03 23:10:07 +03:00
MX
f99c1a8c0a Merge pull request #54 from Ganapati/dev
FlipFrid - upgrade
2022-09-03 19:22:53 +03:00
MX
c41555b579 Merge branch 'fz-dev' into dev 2022-09-03 16:30:13 +03:00
Dig
97b27261d5 fbt: fbtenv_chck_many_source, fix typos + grep logic (#1699)
Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 21:53:43 +09:00
gornekich
1853359d78 [FL-2759], [FL-2766] NFC collect params for mfkey32 attack (#1643)
* nfc: start nfc over rpc
* nfc: add detect reader state
* nfc: add reader analyzer
* nfc: rework reader analyzer
* reader_analyzer: print collected nonces to debug
* reader analyzer: add save on SD card
* reader_analyzer: separate mfkey related part to different file
* mfkey32: add logic for collecting parameters
* nfc: rework pcap with reader analyzer
* nfc: add logger for reader
* nfc: clean up
* nfc: add detect reader view
* nfc: add detect reader and mfkey nonces scenes
* nfc: add mfkey comlplete scene
* nfc: add new assets
* nfc: fix gui
* nfc: fix iso14443-4 UID emulation
* nfc: add no sd card notification
* nfc: fix grammar

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 21:25:36 +09:00
Georgii Surkov
ed2c607dd3 [FL-2776] IR CLI Decode Command (#1692)
* Add decode option to Infrared app
* Implement saving results to file
* Refactor code
* Correct formatting
* Refactor code
* Better command line arguments handling
* Accept generic IR files
* Remove unneeded define

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 19:46:07 +09:00
Max Andreev
bd54c2b342 Fix CI/CD (#1698)
* Test
newline
commit

* Test
newline
commit

* Fix some
variables
and test newline "quotted" commit
2022-09-03 19:18:24 +09:00
Max Andreev
53aa5c71a0 Amap workflow, "toolchain improvements" (#1685)
* fix quotes in amap
* try to fix quotes
* try to read "commit_message"
* Add new actions anv parser
* fix amap_anayse
* fix script ssl error
* test build with new get commit details method
* fix build.yml
* add fbt envs to get_env.py
* fix envs
* using new commit info "way"
* try to fix report link in PR page
* fix "pvs_studio.yml" again
* fix vars
* fix "build.yml"

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 16:09:03 +09:00
Skorpionm
a3932cfa6d [FL-2787] SubGhz: add protocol Clemsa, fix decoder BETT (#1696)
* SubGhz: add protocol Clemsa
* SubGhz: fix decoder BETT protocol

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 15:19:01 +09:00
Sebastian Mauer
1d787e6da8 Add support for Keri tags (#1689)
Co-authored-by: SG <who.just.the.doctor@gmail.com>
2022-09-02 21:36:13 +10:00
Sebastian Mauer
10b0a611cf Add support for Gallagher tags (#1680)
* Add Gallagher protocol
2022-09-02 21:15:34 +10:00
Ganapati
f537ccfe14 Fix warnings 2022-09-01 17:46:08 +02:00
Ganapati
d37dbb29bf Rewrite plugin + new file attack 2022-09-01 17:03:18 +02:00
Skorpionm
0ee4573a65 SubGhz: add protocol Intertechno_V3 (#1622)
* SubGhz: add decode Intertechno_V3
* SubGhz: add encoder Intertechno V3
* SubGhz: add uni_test Intertechno V3
* SubGhz: fix syntax
* SubGhz: add Intertechno V3 dimming mode
* SubGhz: fix parsing event Magellen protocol
* SubGhz: fix syntax
* SubGhz: fix encoder dimm mode

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-08-31 23:27:34 +09:00
Skorpionm
311b60f815 [FL-2771] SubGhz: add protocol Prastel #1674
Co-authored-by: あく <alleteam@gmail.com>
2022-08-31 23:21:36 +09:00
MX
9ff35c9fc3 update audio asset, fix some things
audio asset by @Amec0e
2022-08-31 01:37:01 +03:00
MX
f14874b0e0 update docs and fix led in subghz decode scene 2022-08-30 23:14:43 +03:00
MX
6c7611c57e Fix (LF RFID -> Add manually) names again
remove N/A for PAC (and future protocols with N/A manufacturer)
2022-08-30 21:56:59 +03:00
MX
4d388f4bde SubGhz: add protocol Intertechno_V3 - OFW PR 1622
by Skorpionm
2022-08-30 21:55:25 +03:00
MX
613c729025 Fix displaying LFRFID protocol names - OFW PR 1682
by Astrrra
2022-08-30 21:29:13 +03:00
MX
079cadaeda SubGhz: add protocol Prastel - OFW PR 1674
by Skorpionm
2022-08-30 21:25:08 +03:00
MX
0c0fb57410 Merge branch 'fz-dev' into dev 2022-08-30 21:23:28 +03:00
Skorpionm
e7a5d19f9c [FL-2778] SubGhz: fix CLI "subghz tx" (#1681)
* SubGhz: fix CLI "subghz tx"
* Fix qoutes in amap workflow
* Github: fix step naming
* fix quotes in PR name again

Co-authored-by: あく <alleteam@gmail.com>
Co-authored-by: DrunkBatya <drunkbatya.js@gmail.com>
2022-08-31 00:22:24 +09:00
Eric Betts
8e9043003f Picopass write (#1658)
* [picopass] Prevent false success with non-standard key
* UI for writing
* worker function for write
* Update write command value
* Show card read failure message

Co-authored-by: あく <alleteam@gmail.com>
2022-08-30 22:59:34 +09:00
MX
b67aaad6d5 fix text 2022-08-30 15:33:51 +03:00
Foul
e4c6158d65 Update comment in SConstruct (#1684)
* Update SConstruct: fix Typo
* Grammar in SConstruct

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-08-30 21:15:14 +09:00
MX
23217f4a6a update audio manifest
by @Amec0e
2022-08-30 14:37:44 +03:00
Max Lapan
4fcb90928c ST25TB type is not handled (#1679)
We search for ST25TB type cards, but not handling them being found.
As a result, such cards are detected as NFC-A with 8-byte UID, which lead
to read error on emulation attempt.

Co-authored-by: gornekich <n.gorbadey@gmail.com>
2022-08-30 20:33:05 +09:00
MX
68da151320 Merge branch 'fz-dev' into dev 2022-08-30 13:16:08 +03:00
MX
f70ec8f70e update assets and subghz settings
assets by @Amec0e
2022-08-30 13:15:46 +03:00
Georgii Surkov
5e2a90c6f1 [FL-2773] Fix crash after cancelling Learn New Remote #1675
Co-authored-by: あく <alleteam@gmail.com>
2022-08-30 18:22:14 +09:00
MX
5272b81ae6 Merge branch 'fz-dev' into dev 2022-08-30 11:42:39 +03:00
MX
782593eafc Merge pull request #52 from Kavitate/patch-2
Updated BarcodeGenerator.md
2022-08-30 11:22:13 +03:00
Sebastian Mauer
d76ba20652 Add support for Pyramid tags (#1676)
* Add support for Pyramid tags
* Also add additional checks for AWID decoder to avoid missdetection

* lfrfid worker: reset GPIO_LOAD pin
* lfrfid: protocol viking, format
* lfrfid: protocol pyramid, format
* lfrfid: protocol paradox, format
* lfrfid: protocol jablotron, format
* lfrfid: protocol em4100, format
* lfrfid: increase reading time by 0.5s since protocol viking takes longer to read

Co-authored-by: SG <who.just.the.doctor@gmail.com>
2022-08-30 02:31:28 +10:00
Walter Doekes
611b7e15ed Remove execute permissions from *.c and *.h files (#1651)
* Add permission fix (no execute bit for source files) to fbt lint|format
* Remove execute bit from 59 source files using fbt format
* Also list which permissions are unwanted in lint.py
* Also remove exec permissions from lib/../rfal_nfc.c

Co-authored-by: あく <alleteam@gmail.com>
2022-08-30 01:20:57 +09:00
Sebastian Mauer
274f17ed5a Add support for Viking tags (#1668)
* Add support for Viking tags
Fix blocks_to_write for T5577
2022-08-30 01:19:56 +10:00
Sebastian Mauer
f09c5889dd Add support for Jablotron tags (#1657)
* Add support for Jablotron tags
2022-08-30 01:15:27 +10:00
Sebastian Mauer
39f936ce12 Add support for Paradox tags (#1655)
* Add support for Paradox tags
2022-08-30 01:08:10 +10:00
Sebastian Mauer
1350dcaf63 Add support for PAC/Stanley tags (#1648)
* Add support for PAC/Stanley tags
* Address review comments
2022-08-30 01:04:17 +10:00
Kavitate
4629aee29c Updated BarcodeGenerator.md
Fixed minor spelling and grammatical errors. Also updated the first image to reflect the current correct image.
2022-08-29 09:46:26 -05:00
Himura Kazuto
ffa3ff5e7c Remove obsolete info from docs (#1672) 2022-08-29 00:39:08 +09:00
MX
bacc8fa669 Merge pull request #51 from mothball187/nrfsniffer-improvements
tweak sniff parameters for more speed and reliability
2022-08-28 05:59:06 +03:00
MX
9c748860bb disable wav player, use standard icon in receiver 2022-08-28 05:58:53 +03:00
mothball187
79c4b617c1 tweak sniff parameters for more speed and reliability. clear sniff data on stop or exit. replace preamble GUI option with sample time option 2022-08-27 19:47:10 -07:00
MX
194727515b subghz decode raw gui
by qistoph
2022-08-28 04:46:59 +03:00
MX
c714a32ea5 Merge branch 'fz-dev' into dev 2022-08-27 16:17:16 +03:00
Max Andreev
689da15346 workflows and fbtenv improovements (#1661)
* add --restore option, improove clearing
* fix trap
* fix unset
* fix fbtenv clearing
* disabling pvs studio and amap analyses in forks, fbtenv.sh fixes
* fbtenv fix

Co-authored-by: あく <alleteam@gmail.com>
2022-08-27 21:38:13 +09:00
Skorpionm
1a4a6d4625 [FL-2769] SubGhz: out debug data to external pin #1665
Co-authored-by: あく <alleteam@gmail.com>
2022-08-27 17:06:25 +09:00
SG
99a7d06f71 Speedup SD card & enlarge your RAM. (#1649)
* FuriHal: sram2 memory manager
* FuriHal: sram2 memory allocator
* FuriHal: allow NULL buffers for txrx in spi hal
* SD card: sector cache
* FuriHal: fix init in memory hal
* RPC: STARTUP instead SERVICE
* Memory: pool "free" command
* Thread: service can be statically allocated in a memory pool

Co-authored-by: あく <alleteam@gmail.com>
2022-08-27 13:25:47 +09:00
MX
db4976c501 update ac asset
by @Amec0e
2022-08-27 05:15:49 +03:00
MX
fc62762ce4 fix assets header, doesnt affect bruteforce work 2022-08-27 04:41:35 +03:00
MX
a6597da4a5 Merge remote-tracking branch 'origin/dev' into dev 2022-08-27 03:16:37 +03:00
MX
ab5bcd71f5 update assets and changelog 2022-08-27 03:15:59 +03:00
MX
eb28dc2e20 Merge pull request #50 from darmiel/fix/infrared-buttons
fix[infrared]: fixed crash if button out of bounds and sending wrong signals
2022-08-27 03:06:15 +03:00
Daniel
92db5e1afe Merge remote-tracking branch 'origin/fix/infrared-buttons' into fix/infrared-buttons 2022-08-27 01:39:36 +02:00
Daniel
885bb0c730 fix[infrared]: clear records on remote switch 2022-08-27 01:39:06 +02:00
Daniel
71f4bd0a7c Merge branch 'Eng1n33r:dev' into fix/infrared-buttons 2022-08-27 01:06:33 +02:00
MX
66e361714f Merge branch 'fz-dev' into dev 2022-08-27 02:06:06 +03:00
Daniel
9bd6d956ca fix[infrared]: select first button on remote change 2022-08-27 00:46:34 +02:00
MX
293d5f722d update changelog & docs 2022-08-26 20:15:15 +03:00
MX
04f522487e Universal remote for fans, new buttons for AC
updated assets by @Amec0e
2022-08-26 20:02:25 +03:00
Yukai Li
ab4bb55d0f nfc: Change furi_assert to furi_crash for default switch cases (#1662)
* nfc: Change furi_assert to furi_crash for default switch cases
* Nfc: change MiFare Ultralight crash message

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-08-26 01:07:54 +09:00
MX
64edddeabf new spectrum analyzer icon
by @Svaarich
2022-08-25 17:39:55 +03:00
MX
a0819df874 update changelog 2022-08-25 14:36:51 +03:00
MX
24265204b3 Merge pull request #49 from darmiel/feat/stealth-frequency-analyzer
feat[freq-analzer]: add stealth modes for frequency analyzer
2022-08-25 14:12:51 +03:00
Daniel
e828d15fe3 feat[freq-analzer]: added ui for feedbacl level 2022-08-25 08:57:31 +02:00
MX
6c2cf68077 update tv asset
by @Amec0e
2022-08-25 05:31:47 +03:00
MX
f6d38939c2 hide txt extension in unirf browser 2022-08-25 05:13:08 +03:00
MX
f9d3d6bf5c fix unirf crash, fix keeloq seed display, fix flipfrid
and add new unirf icon(animated) by Svarich
2022-08-25 03:21:44 +03:00
Daniel
33176220f6 feat[freq-analzer]: added feedback levels 0, 1, 2
0 - no feedback
1 - vibro only
2 - vibro and beep
switch the modes with the "UP" key
2022-08-25 01:24:11 +02:00
MX
b31e4a36a8 Merge pull request #48 from Ganapati/dev
Add flipfrid application
2022-08-24 23:16:46 +03:00
MX
ecbe42744d Merge pull request #47 from darmiel/feat/unirf-protocols
feat[unirfremix]: allow to send dynamic protocols
2022-08-24 23:16:13 +03:00
MX
7abc49ea21 Implement TX while button is hold, other small changes
Repeat functionality removed since all buttons adds event in queue to be executed after tx is finished
2022-08-24 23:14:33 +03:00
MX
5b6f2cb80d Merge branch 'dev' into feat/unirf-protocols 2022-08-24 23:11:17 +03:00
MX
1f68cf09eb Merge branch 'fz-dev' into dev 2022-08-24 23:10:57 +03:00
Gana (@G4N4P4T1)
273871013e Add flipfrid application 2022-08-24 18:14:03 +02:00
Skorpionm
ce7b943793 [FL-2764] SubGhz: fix CAME, Chamberlain potocol (#1650)
* SubGhz: fix guard time CAME potocol
* SubGhz: fix file upload Chamberlain
* Github: fix spelling

Co-authored-by: あく <alleteam@gmail.com>
2022-08-25 00:14:27 +09:00
SG
7e20df7e93 LFRFID RC fixes (#1652)
* lfrid: fix write validation
* lfrid app: restore key data after write
2022-08-24 23:41:59 +09:00
Daniel
9ec0835012 fix[unirf]: removed leftover debug messages 2022-08-24 14:36:50 +02:00
Daniel
432782344a feat[unirf]: blinking on send
* fixed a HardFault when trying to `subghz_protocol_decoder_base_get_string`
* don't enter `power_suppress_charge` for each sending individually
2022-08-24 14:28:27 +02:00
Daniel Statzner
64ee9dd674 Merge branch 'Eng1n33r:dev' into feat/unirf-protocols 2022-08-23 22:39:45 +02:00
Daniel
803422c18e fix[unirf]: fixed dynamic protocol, refactoring 2022-08-23 20:47:54 +02:00
Daniel
545dabadb7 refactor[unirf]: moved environment to struct 2022-08-23 14:48:08 +02:00
Daniel
73c28437d6 fix[unirf]: display decoded after sending 2022-08-23 09:13:41 +02:00
Daniel
71c27de8cc fix[unirf]: load environment mf codes 2022-08-19 03:38:35 +02:00
Daniel
9eb9a44ee4 Merge remote-tracking branch 'upstream/dev' into feat/unirf-protocols 2022-08-18 01:46:57 +02:00
Daniel
3aed4de1b4 fix[unirf]: passed wrong FlipperFormat 2022-08-15 20:52:04 +02:00
Daniel
33fbf268b7 fix[unirf]: close fff_file 2022-08-14 23:52:05 +02:00
Daniel
36784f95c8 refactor[unirf]: dynamic protocol updates 2022-08-14 23:26:49 +02:00
Daniel
be942ef7b2 feat[unirfremix]: allow protocols other than raw 2022-08-12 01:55:19 +02:00
261 changed files with 10112 additions and 2084 deletions

View File

@@ -36,6 +36,7 @@ steps:
- mkdir -p sd-card/nfc/assets
- mkdir -p sd-card/infrared/assets
- mkdir -p sd-card/unirf
- mkdir -p sd-card/rfidfuzzer
- mkdir -p sd-card/badusb/layouts
- cp assets/resources/badusb/layouts/* sd-card/badusb/layouts/
- cp assets/resources/subghz/assets/dangerous_settings sd-card/subghz/assets/dangerous_settings
@@ -44,9 +45,11 @@ steps:
- cp assets/resources/nfc/assets/mf_classic_dict.nfc sd-card/nfc/assets/mf_classic_dict.nfc
- cp assets/resources/infrared/assets/tv.ir sd-card/infrared/assets/tv.ir
- cp assets/resources/infrared/assets/ac.ir sd-card/infrared/assets/ac.ir
- cp assets/resources/infrared/assets/fans.ir sd-card/infrared/assets/fans.ir
- cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir
- cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir
- cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt
- cp assets/resources/rfidfuzzer/example_uids.txt sd-card/rfidfuzzer/example_uids.txt
- cp assets/resources/Manifest sd-card/Manifest
- zip -r artifacts-default/sd-card-${DRONE_TAG}.zip sd-card
- rm -rf sd-card
@@ -115,9 +118,7 @@ steps:
[-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
document:
- artifacts-default/flipper-z-f7-full-${DRONE_TAG}.dfu
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip
- artifacts-default/sd-card-${DRONE_TAG}.zip
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz
- name: "Send discord notification"
image: appleboy/drone-discord

View File

@@ -1,24 +1,12 @@
### New changes
* New universal remote for projectors
* OFW: New LF-RFID subsystem (New protocols, Animal tags support)
* Updated universal remote assets (by @Amec0e)
* Renamed UniRF Remix -> Sub-GHz Remote
* Replaced Hex/Dec converter with Multi Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
* New update screen, readme pictures (by @Svaarich)
* Fixed crash if Center button is pressed on the "update success" screen via screensharing
* Temporary disabled one log call in picopass plugin to fix crash/freeze on Read screen
* OFW: Picopass load/info/delete
* OFW: SubGhz: add protocol Magellen
* OFW: Fix mifare ultralight/ntag unlock
* OFW: Dolphin level thresholds update
* OFW: Add MFC 1/4K 4/7bUID to "Add Manually"
* OFW: Other fixes and changes
* WAV Player, Arkanoid, TicTacToe, Barcode generator - plugins enabled and included in releases again
* Debug apps disabled in release build
* WAV Player moved to Games menu
* OFW: Lib: update LFS to v2.5.0, lower update free page limit
**Note: Prefer installing using web updater or by self update package, all needed assets will be installed**
**Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed**
**Build naming has been changed - all same as before but `cg - codegrabber` changed to `un - unleashed`**
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip`
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or `.tgz` for iOS mobile app
DFU for update using qFlipper - `flipper-z-f7-full-(version).dfu`

View File

@@ -4,10 +4,10 @@
</a>
</h3>
Welcome to Flipper Zero's Custom Firmware repo!
### Welcome to Flipper Zero Unleashed Firmware repo!
Our goal is to make any features possible in this device without any limitations!
Please help us implement emulation for all subghz dynamic (rolling code) protocols and static code brute-force app!
Please help us implement emulation for all subghz dynamic (rolling code) protocols and static code brute-force plugin!
<br>
@@ -31,21 +31,20 @@ Our Discord Community:
* Extra SubGHz frequencies + extra Mifare Classic keys
* Picopass/iClass plugin included in releases
* Recompiled IR TV Universal Remote for ALL buttons
* Universal A/C and Audio(soundbars, etc.) remote
* Universal remote for Projectors
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
* BadUSB keyboard layouts
* Customizable Flipper name
* Other small fixes and changes throughout
See changelog in releases for latest updates!
### Current modified and new SubGhz protocols list:
### Current modified and new SubGHz protocols list:
- HCS101
- An-Motors
- CAME Atomo
- FAAC SLH (Spa) [if cloning existing remote - external seed calculation required]
- BFT Mitto [if cloning existing remote - external seed calculation required]
- Keeloq (+ proper manufacturer codes selection) [Not ALL systems supported yet!]
- FAAC SLH (Spa) [External seed calculation required]
- BFT Mitto [External seed calculation required]
- Keeloq [Not ALL systems supported yet!]
- Nice Flor S
- Security+ v1 & v2
- Star Line
@@ -56,8 +55,6 @@ See changelog in releases for latest updates!
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
**Big thanks to all sponsors!**
### Community apps included:
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
@@ -68,8 +65,8 @@ See changelog in releases for latest updates!
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
- Simple Clock (fixed) !! New version WIP, wait for updates !! [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- UniversalRF Remix (with changes)(only RAW subghz files) [(by ESurge)(Original UniversalRF by jimilinuxguy)](https://github.com/ESurge/flipperzero-firmware-unirfremix)
- Simple Clock (fixed) [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- UniversalRF Remix / Sub-GHz Remote [(by ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix)[(updated and all protocol support added by darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols)
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)
- Arkanoid (with fixes) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)

View File

@@ -1,5 +1,5 @@
#
# Main Fipper Build System entry point
# Main Flipper Build System entry point
#
# This file is evaluated by scons (the build system) every time fbt is invoked.
# Scons constructs all referenced environments & their targets' dependency
@@ -15,7 +15,7 @@ DefaultEnvironment(tools=[])
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
# This environment is created only for loading options & validating file/dir existance
# This environment is created only for loading options & validating file/dir existence
fbt_variables = SConscript("site_scons/commandline.scons")
cmd_environment = Environment(tools=[], variables=fbt_variables)
Help(fbt_variables.GenerateHelpText(cmd_environment))

0
applications/bad_usb/views/bad_usb_view.h Executable file → Normal file
View File

0
applications/bt/bt_debug_app/views/bt_carrier_test.c Executable file → Normal file
View File

0
applications/bt/bt_debug_app/views/bt_test.c Executable file → Normal file
View File

0
applications/bt/bt_debug_app/views/bt_test.h Executable file → Normal file
View File

0
applications/bt/bt_hid_app/views/bt_hid_keynote.c Executable file → Normal file
View File

0
applications/bt/bt_hid_app/views/bt_hid_media.c Executable file → Normal file
View File

0
applications/bt/bt_settings_app/bt_settings_app.c Executable file → Normal file
View File

0
applications/bt/bt_settings_app/bt_settings_app.h Executable file → Normal file
View File

View File

View File

View File

@@ -281,6 +281,9 @@ void cli_command_free(Cli* cli, string_t args, void* context) {
printf("Total heap size: %d\r\n", memmgr_get_total_heap());
printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
printf("Pool free: %d\r\n", memmgr_pool_get_free());
printf("Maximum pool block: %d\r\n", memmgr_pool_get_max_block());
}
void cli_command_free_blocks(Cli* cli, string_t args, void* context) {

0
applications/cli/cli_i.h Executable file → Normal file
View File

View File

@@ -0,0 +1,8 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* @G4N4P4T1 wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/

View File

@@ -0,0 +1,21 @@
# Flipfrid
Basic EM4100 Fuzzer
## Why
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz).
Objective is to provide a simple to use fuzzer to test readers by emulating various cards.
EM4100 cards use a 1 byte customer id and 4 bytes card id.
## How
There is 4 modes :
- Default key loop over 16 factory/default keys and emulate each one after one ;
- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ;
- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
- Uids list: loop over a text file (one uid per line)
TODO :
- blank screen on back press

View File

@@ -0,0 +1,10 @@
App(
appid="flipfrid",
name="RFID Fuzzer",
apptype=FlipperAppType.PLUGIN,
entry_point="flipfrid_start",
cdefines=["APP_FLIP_FRID"],
requires=["gui"],
stack_size=1 * 1024,
order=29,
)

View File

@@ -0,0 +1,258 @@
#include "flipfrid.h"
#include "scene/flipfrid_scene_entrypoint.h"
#include "scene/flipfrid_scene_load_file.h"
#include "scene/flipfrid_scene_select_field.h"
#include "scene/flipfrid_scene_run_attack.h"
#include "scene/flipfrid_scene_load_custom_uids.h"
#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer"
static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) {
FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100);
if(flipfrid_state == NULL) {
return;
}
// Draw correct Canvas
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_draw(canvas, flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_draw(canvas, flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state);
break;
}
release_mutex((ValueMutex*)ctx, flipfrid_state);
}
void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
FlipFridEvent event = {
.evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type};
furi_message_queue_put(event_queue, &event, 25);
}
static void flipfrid_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
FlipFridEvent event = {
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
furi_message_queue_put(event_queue, &event, 25);
}
FlipFridState* flipfrid_alloc() {
FlipFridState* flipfrid = malloc(sizeof(FlipFridState));
string_init(flipfrid->notification_msg);
string_init(flipfrid->attack_name);
flipfrid->previous_scene = NoneScene;
flipfrid->current_scene = SceneEntryPoint;
flipfrid->is_running = true;
flipfrid->is_attacking = false;
flipfrid->key_index = 0;
flipfrid->menu_index = 0;
flipfrid->attack = FlipFridAttackDefaultValues;
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
flipfrid->data[0] = 0x00;
flipfrid->data[1] = 0x00;
flipfrid->data[2] = 0x00;
flipfrid->data[3] = 0x00;
flipfrid->data[4] = 0x00;
flipfrid->payload[0] = 0x00;
flipfrid->payload[1] = 0x00;
flipfrid->payload[2] = 0x00;
flipfrid->payload[3] = 0x00;
flipfrid->payload[4] = 0x00;
//Dialog
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);
return flipfrid;
}
void flipfrid_free(FlipFridState* flipfrid) {
//Dialog
furi_record_close(RECORD_DIALOGS);
notification_message(flipfrid->notify, &sequence_blink_stop);
// Strings
string_clear(flipfrid->notification_msg);
string_clear(flipfrid->attack_name);
free(flipfrid->data);
free(flipfrid->payload);
// The rest
free(flipfrid);
}
// ENTRYPOINT
int32_t flipfrid_start(void* p) {
UNUSED(p);
// Input
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent));
FlipFridState* flipfrid_state = flipfrid_alloc();
ValueMutex flipfrid_state_mutex;
// Mutex
FURI_LOG_I(TAG, "Initializing flipfrid mutex");
if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
free(flipfrid_state);
return 255;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) {
FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER);
}
furi_record_close(RECORD_STORAGE);
// Configure view port
FURI_LOG_I(TAG, "Initializing viewport");
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex);
view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue);
// Configure timer
FURI_LOG_I(TAG, "Initializing timer");
FuriTimer* timer =
furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
// Register view port in GUI
FURI_LOG_I(TAG, "Initializing gui");
Gui* gui = (Gui*)furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Init values
FlipFridEvent event;
while(flipfrid_state->is_running) {
// Get next event
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
if(event_status == FuriStatusOk) {
if(event.evt_type == EventTypeKey) {
//Handle event key
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_event(event, flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_event(event, flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_event(event, flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_event(event, flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state);
break;
}
} else if(event.evt_type == EventTypeTick) {
//Handle event tick
if(flipfrid_state->current_scene != flipfrid_state->previous_scene) {
// Trigger Exit Scene
switch(flipfrid_state->previous_scene) {
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_exit(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_exit(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_exit(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_exit(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_exit(flipfrid_state);
break;
case NoneScene:
break;
}
// Trigger Entry Scene
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_enter(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_enter(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_enter(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_enter(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_enter(flipfrid_state);
break;
}
flipfrid_state->previous_scene = flipfrid_state->current_scene;
}
// Trigger Tick Scene
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_tick(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_tick(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_tick(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_tick(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_tick(flipfrid_state);
break;
}
view_port_update(view_port);
}
}
}
// Cleanup
furi_timer_stop(timer);
furi_timer_free(timer);
FURI_LOG_I(TAG, "Cleaning up");
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
flipfrid_free(flipfrid_state);
return 0;
}

View File

@@ -0,0 +1,76 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <gui/gui.h>
#include <gui/modules/submenu.h>
#include "m-string.h"
#include <dialogs/dialogs.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <toolbox/stream/stream.h>
#include <flipper_format/flipper_format_i.h>
#include <toolbox/stream/stream.h>
#include <toolbox/stream/string_stream.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <lib/lfrfid/lfrfid_worker.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#define TAG "FlipFrid"
typedef enum {
FlipFridAttackDefaultValues,
FlipFridAttackBfCustomerId,
FlipFridAttackLoadFile,
FlipFridAttackLoadFileCustomUids,
} FlipFridAttacks;
typedef enum {
NoneScene,
SceneEntryPoint,
SceneSelectFile,
SceneSelectField,
SceneAttack,
SceneLoadCustomUids,
} FlipFridScene;
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType evt_type;
InputKey key;
InputType input_type;
} FlipFridEvent;
// STRUCTS
typedef struct {
bool is_running;
bool is_attacking;
FlipFridScene current_scene;
FlipFridScene previous_scene;
NotificationApp* notify;
u_int8_t menu_index;
string_t data_str;
uint8_t data[5];
uint8_t payload[5];
uint8_t attack_step;
FlipFridAttacks attack;
string_t attack_name;
DialogsApp* dialogs;
string_t notification_msg;
uint8_t key_index;
LFRFIDWorker* worker;
ProtocolDict* dict;
ProtocolId protocol;
// Used for custom dictionnary
Stream* uids_stream;
} FlipFridState;

View File

@@ -0,0 +1,123 @@
#include "flipfrid_scene_entrypoint.h"
string_t menu_items[4];
void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) {
switch(index) {
case FlipFridAttackDefaultValues:
context->attack = FlipFridAttackDefaultValues;
context->current_scene = SceneAttack;
string_set_str(context->attack_name, "Default Values");
break;
case FlipFridAttackBfCustomerId:
context->attack = FlipFridAttackBfCustomerId;
context->current_scene = SceneAttack;
string_set_str(context->attack_name, "Bad Customer ID");
break;
case FlipFridAttackLoadFile:
context->attack = FlipFridAttackLoadFile;
context->current_scene = SceneSelectFile;
string_set_str(context->attack_name, "Load File");
break;
case FlipFridAttackLoadFileCustomUids:
context->attack = FlipFridAttackLoadFileCustomUids;
context->current_scene = SceneLoadCustomUids;
string_set_str(context->attack_name, "Load Custom UIDs");
break;
default:
break;
}
}
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
// Clear the previous payload
context->payload[0] = 0x00;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 0x00;
context->menu_index = 0;
for(uint32_t i = 0; i < 4; i++) {
string_init(menu_items[i]);
}
string_set(menu_items[0], "Default Values");
string_set(menu_items[1], "BF Customer ID");
string_set(menu_items[2], "Load File");
string_set(menu_items[3], "Load uids from file");
}
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
UNUSED(context);
for(uint32_t i = 0; i < 4; i++) {
string_clear(menu_items[i]);
}
}
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
context->menu_index++;
}
break;
case InputKeyUp:
if(context->menu_index > FlipFridAttackDefaultValues) {
context->menu_index--;
}
break;
case InputKeyLeft:
case InputKeyRight:
break;
case InputKeyOk:
flipfrid_scene_entrypoint_menu_callback(context, context->menu_index);
break;
case InputKeyBack:
context->is_running = false;
break;
}
}
}
}
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer");
if(context->menu_index > FlipFridAttackDefaultValues) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
64,
24,
AlignCenter,
AlignTop,
string_get_cstr(menu_items[context->menu_index - 1]));
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 36, AlignCenter, AlignTop, string_get_cstr(menu_items[context->menu_index]));
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
64,
48,
AlignCenter,
AlignTop,
string_get_cstr(menu_items[context->menu_index + 1]));
}
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context);
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context);
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context);
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context);

View File

@@ -0,0 +1,79 @@
#include "flipfrid_scene_load_custom_uids.h"
#include "flipfrid_scene_run_attack.h"
#include "flipfrid_scene_entrypoint.h"
#define LFRFID_UIDS_EXTENSION ".txt"
#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer"
bool flipfrid_load_uids(FlipFridState* context, const char* file_path) {
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
context->uids_stream = buffered_file_stream_alloc(storage);
result =
buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING);
// Close if loading fails
if(!result) {
buffered_file_stream_close(context->uids_stream);
return false;
}
return result;
}
bool flipfrid_load_custom_uids_from_file(FlipFridState* context) {
// Input events and views are managed by file_select
string_t uid_path;
string_init(uid_path);
string_set_str(uid_path, RFIDFUZZER_APP_PATH_FOLDER);
bool res = dialog_file_browser_show(
context->dialogs, uid_path, uid_path, LFRFID_UIDS_EXTENSION, true, &I_125_10px, false);
if(res) {
res = flipfrid_load_uids(context, string_get_cstr(uid_path));
}
string_clear(uid_path);
return res;
}
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) {
if(flipfrid_load_custom_uids_from_file(context)) {
// Force context loading
flipfrid_scene_run_attack_on_enter(context);
context->current_scene = SceneAttack;
} else {
flipfrid_scene_entrypoint_on_enter(context);
context->current_scene = SceneEntryPoint;
}
}
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
case InputKeyOk:
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
}
}
}
}
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) {
UNUSED(context);
UNUSED(canvas);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context);
bool flipfrid_load_custom_uids_from_file(FlipFridState* context);

View File

@@ -0,0 +1,146 @@
#include "flipfrid_scene_load_file.h"
#include "flipfrid_scene_entrypoint.h"
#define LFRFID_APP_EXTENSION ".rfid"
#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid"
bool flipfrid_load(FlipFridState* context, const char* file_path) {
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
string_t temp_str;
string_init(temp_str);
do {
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_E(TAG, "Error open file %s", file_path);
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Error open file");
break;
}
// FileType
if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Filetype");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Filetypes");
break;
} else {
FURI_LOG_I(TAG, "Filetype: %s", string_get_cstr(temp_str));
}
// Key type
if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Key type");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Key type");
break;
} else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
FURI_LOG_E(TAG, "Unsupported Key type");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Unsupported Key type");
break;
}
}
// Data
if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Data");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Key");
break;
} else {
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
// Check data size
if(string_size(context->data_str) != 14) {
FURI_LOG_E(TAG, "Incorrect Key length");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Incorrect Key length");
break;
}
// String to uint8_t
for(uint8_t i = 0; i < 5; i++) {
char temp_str2[3];
temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];
temp_str2[2] = '\0';
context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16);
}
}
result = true;
} while(0);
string_clear(temp_str);
flipper_format_free(fff_data_file);
if(result) {
FURI_LOG_I(TAG, "Loaded successfully");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Source loaded.");
}
return result;
}
void flipfrid_scene_load_file_on_enter(FlipFridState* context) {
if(flipfrid_load_protocol_from_file(context)) {
context->current_scene = SceneSelectField;
} else {
flipfrid_scene_entrypoint_on_enter(context);
context->current_scene = SceneEntryPoint;
}
}
void flipfrid_scene_load_file_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_file_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
case InputKeyOk:
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
}
}
}
}
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) {
UNUSED(context);
UNUSED(canvas);
}
bool flipfrid_load_protocol_from_file(FlipFridState* context) {
string_t user_file_path;
string_init(user_file_path);
string_set_str(user_file_path, LFRFID_APP_PATH_FOLDER);
// Input events and views are managed by file_select
bool res = dialog_file_browser_show(
context->dialogs,
user_file_path,
user_file_path,
LFRFID_APP_EXTENSION,
true,
&I_125_10px,
true);
if(res) {
res = flipfrid_load(context, string_get_cstr(user_file_path));
}
string_clear(user_file_path);
return res;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_load_file_on_enter(FlipFridState* context);
void flipfrid_scene_load_file_on_exit(FlipFridState* context);
void flipfrid_scene_load_file_on_tick(FlipFridState* context);
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context);
bool flipfrid_load_protocol_from_file(FlipFridState* context);

View File

@@ -0,0 +1,212 @@
#include "flipfrid_scene_run_attack.h"
uint8_t counter = 0;
#define TIME_BETWEEN_CARDS 5
uint8_t id_list[16][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
{0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
{0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
{0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
{0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
{0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
{0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
};
void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
context->attack_step = 0;
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
context->worker = lfrfid_worker_alloc(context->dict);
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
}
void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
lfrfid_worker_stop(context->worker);
lfrfid_worker_stop_thread(context->worker);
lfrfid_worker_free(context->worker);
protocol_dict_free(context->dict);
notification_message(context->notify, &sequence_blink_stop);
}
void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
if(context->is_attacking) {
if(1 == counter) {
protocol_dict_set_data(context->dict, context->protocol, context->payload, 5);
lfrfid_worker_free(context->worker);
context->worker = lfrfid_worker_alloc(context->dict);
lfrfid_worker_start_thread(context->worker);
lfrfid_worker_emulate_start(context->worker, context->protocol);
} else if(0 == counter) {
lfrfid_worker_stop(context->worker);
lfrfid_worker_stop_thread(context->worker);
switch(context->attack) {
case FlipFridAttackDefaultValues:
context->payload[0] = id_list[context->attack_step][0];
context->payload[1] = id_list[context->attack_step][1];
context->payload[2] = id_list[context->attack_step][2];
context->payload[3] = id_list[context->attack_step][3];
context->payload[4] = id_list[context->attack_step][4];
if(context->attack_step == 15) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
} else {
context->attack_step++;
}
break;
case FlipFridAttackBfCustomerId:
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 0x00;
if(context->attack_step == 255) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
} else {
context->attack_step++;
}
break;
case FlipFridAttackLoadFile:
context->payload[0] = context->data[0];
context->payload[1] = context->data[1];
context->payload[2] = context->data[2];
context->payload[3] = context->data[3];
context->payload[4] = context->data[4];
context->payload[context->key_index] = context->attack_step;
if(context->attack_step == 255) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
break;
} else {
context->attack_step++;
}
break;
case FlipFridAttackLoadFileCustomUids:
while(true) {
string_reset(context->data_str);
if(!stream_read_line(context->uids_stream, context->data_str)) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
break;
};
if(string_get_char(context->data_str, 0) == '#') continue;
if(string_size(context->data_str) != 11) continue;
break;
}
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 5; i++) {
char temp_str[3];
temp_str[0] = string_get_cstr(context->data_str)[i * 2];
temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
temp_str[2] = '\0';
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
}
break;
}
}
if(counter > TIME_BETWEEN_CARDS) {
counter = 0;
} else {
counter++;
}
}
}
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
break;
case InputKeyOk:
counter = 0;
if(!context->is_attacking) {
notification_message(context->notify, &sequence_blink_start_blue);
context->is_attacking = true;
} else {
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
}
break;
case InputKeyBack:
if(context->attack == FlipFridAttackLoadFileCustomUids) {
buffered_file_stream_close(context->uids_stream);
}
context->attack_step = 0;
context->is_attacking = false;
string_reset(context->notification_msg);
context->current_scene = SceneEntryPoint;
notification_message(context->notify, &sequence_blink_stop);
break;
}
}
}
}
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Frame
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
char uid[16];
snprintf(
uid,
sizeof(uid),
"%02X:%02X:%02X:%02X:%02X",
context->payload[0],
context->payload[1],
context->payload[2],
context->payload[3],
context->payload[4]);
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary);
char start_stop_msg[20];
if(context->is_attacking) {
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop ");
} else {
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to start ");
}
canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg);
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_run_attack_on_enter(FlipFridState* context);
void flipfrid_scene_run_attack_on_exit(FlipFridState* context);
void flipfrid_scene_run_attack_on_tick(FlipFridState* context);
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context);

View File

@@ -0,0 +1,121 @@
#include "flipfrid_scene_select_field.h"
void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) {
const char* key_cstr = string_get_cstr(context->data_str);
uint8_t str_index = (index * 3);
char display_menu[17] = {
'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'};
if(index > 1) {
display_menu[0] = key_cstr[str_index - 6];
display_menu[1] = key_cstr[str_index - 5];
} else {
display_menu[0] = ' ';
display_menu[1] = ' ';
}
if(index > 0) {
display_menu[3] = key_cstr[str_index - 3];
display_menu[4] = key_cstr[str_index - 2];
} else {
display_menu[3] = ' ';
display_menu[4] = ' ';
}
display_menu[7] = key_cstr[str_index];
display_menu[8] = key_cstr[str_index + 1];
if((str_index + 4) <= (uint8_t)strlen(key_cstr)) {
display_menu[11] = key_cstr[str_index + 3];
display_menu[12] = key_cstr[str_index + 4];
} else {
display_menu[11] = ' ';
display_menu[12] = ' ';
}
if((str_index + 8) <= (uint8_t)strlen(key_cstr)) {
display_menu[14] = key_cstr[str_index + 6];
display_menu[15] = key_cstr[str_index + 7];
} else {
display_menu[14] = ' ';
display_menu[15] = ' ';
}
string_reset(context->notification_msg);
string_set_str(context->notification_msg, display_menu);
}
void flipfrid_scene_select_field_on_enter(FlipFridState* context) {
string_clear(context->notification_msg);
}
void flipfrid_scene_select_field_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_select_field_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
const char* key_cstr = string_get_cstr(context->data_str);
// don't look, it's ugly but I'm a python dev so...
uint8_t nb_bytes = 0;
for(uint8_t i = 0; i < strlen(key_cstr); i++) {
if(' ' == key_cstr[i]) {
nb_bytes++;
}
}
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
break;
case InputKeyLeft:
if(context->key_index > 0) {
context->key_index = context->key_index - 1;
}
break;
case InputKeyRight:
if(context->key_index < nb_bytes) {
context->key_index = context->key_index + 1;
}
break;
case InputKeyOk:
string_reset(context->notification_msg);
context->current_scene = SceneAttack;
break;
case InputKeyBack:
string_reset(context->notification_msg);
context->current_scene = SceneSelectFile;
break;
}
FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes);
}
}
}
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Frame
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Use < > to select byte.");
char msg_index[18];
snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index);
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index);
flipfrid_center_displayed_key(context, context->key_index);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg));
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_select_field_on_enter(FlipFridState* context);
void flipfrid_scene_select_field_on_exit(FlipFridState* context);
void flipfrid_scene_select_field_on_tick(FlipFridState* context);
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context);
void center_displayed_key(FlipFridState* context, uint8_t index);

0
applications/gpio/views/gpio_test.c Executable file → Normal file
View File

0
applications/gpio/views/gpio_test.h Executable file → Normal file
View File

0
applications/gpio/views/gpio_usb_uart.h Executable file → Normal file
View File

0
applications/gui/canvas.h Executable file → Normal file
View File

View File

@@ -83,6 +83,15 @@ ButtonPanel* button_panel_alloc() {
return button_panel;
}
void button_panel_reset_selection(ButtonPanel* button_panel) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
model->selected_item_x = 0;
model->selected_item_y = 0;
return true;
});
}
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) {
furi_check(reserve_x > 0);
furi_check(reserve_y > 0);

View File

@@ -35,6 +35,12 @@ void button_panel_free(ButtonPanel* button_panel);
*/
void button_panel_reset(ButtonPanel* button_panel);
/** Resets selected_item_x and selected_item_y.
*
* @param button_panel ButtonPanel instance
*/
void button_panel_reset_selection(ButtonPanel* button_panel);
/** Reserve space for adding items.
*
* One does not simply use button_panel_add_item() without this function. It

0
applications/gui/modules/dialog_ex.c Executable file → Normal file
View File

0
applications/gui/modules/menu.h Executable file → Normal file
View File

0
applications/gui/modules/text_box.c Executable file → Normal file
View File

0
applications/gui/modules/text_box.h Executable file → Normal file
View File

0
applications/gui/modules/variable_item_list.h Executable file → Normal file
View File

0
applications/gui/modules/widget.h Executable file → Normal file
View File

View File

View File

View File

0
applications/gui/scene_manager.c Executable file → Normal file
View File

0
applications/gui/scene_manager.h Executable file → Normal file
View File

0
applications/gui/scene_manager_i.h Executable file → Normal file
View File

0
applications/gui/view_dispatcher.h Executable file → Normal file
View File

View File

@@ -35,6 +35,10 @@ InfraredBruteForce* infrared_brute_force_alloc() {
return brute_force;
}
void infrared_brute_force_clear_records(InfraredBruteForce* brute_force) {
InfraredBruteForceRecordDict_reset(brute_force->records);
}
void infrared_brute_force_free(InfraredBruteForce* brute_force) {
furi_assert(!brute_force->ff);
InfraredBruteForceRecordDict_clear(brute_force->records);

View File

@@ -16,6 +16,7 @@ bool infrared_brute_force_start(
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force);
void infrared_brute_force_stop(InfraredBruteForce* brute_force);
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force);
void infrared_brute_force_clear_records(InfraredBruteForce* brute_force);
void infrared_brute_force_add_record(
InfraredBruteForce* brute_force,
uint32_t index,

View File

@@ -3,6 +3,8 @@
#include <infrared.h>
#include <infrared_worker.h>
#include <furi_hal_infrared.h>
#include <flipper_format.h>
#include <toolbox/args.h>
#include "infrared_signal.h"
@@ -10,6 +12,7 @@
static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
static void infrared_cli_process_decode(Cli* cli, string_t args);
static const struct {
const char* cmd;
@@ -17,6 +20,7 @@ static const struct {
} infrared_cli_commands[] = {
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
{.cmd = "decode", .process_function = infrared_cli_process_decode},
};
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
@@ -86,6 +90,7 @@ static void infrared_cli_print_usage(void) {
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
INFRARED_MIN_FREQUENCY,
INFRARED_MAX_FREQUENCY);
printf("\tir decode <input_file> [<output_file>]\r\n");
}
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
@@ -162,6 +167,160 @@ static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
infrared_signal_free(signal);
}
static bool
infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
bool ret = infrared_signal_save(signal, file, name);
if(!ret) {
printf("Failed to save signal: \"%s\"\r\n", name);
}
return ret;
}
static bool infrared_cli_decode_raw_signal(
InfraredRawSignal* raw_signal,
InfraredDecoderHandler* decoder,
FlipperFormat* output_file,
const char* signal_name) {
InfraredSignal* signal = infrared_signal_alloc();
bool ret = false, level = true, is_decoded = false;
size_t i;
for(i = 0; i < raw_signal->timings_size; ++i) {
// TODO: Any infrared_check_decoder_ready() magic?
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
if(message) {
is_decoded = true;
printf(
"Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
infrared_get_protocol_name(message->protocol),
message->address,
message->command,
(message->repeat ? "R" : ""));
if(output_file && !message->repeat) {
infrared_signal_set_message(signal, message);
if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
}
}
level = !level;
}
if(i == raw_signal->timings_size) {
if(!is_decoded && output_file) {
infrared_signal_set_raw_signal(
signal,
raw_signal->timings,
raw_signal->timings_size,
raw_signal->frequency,
raw_signal->duty_cycle);
ret = infrared_cli_save_signal(signal, output_file, signal_name);
} else {
ret = true;
}
}
infrared_reset_decoder(decoder);
infrared_signal_free(signal);
return ret;
}
static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
bool ret = false;
InfraredSignal* signal = infrared_signal_alloc();
InfraredDecoderHandler* decoder = infrared_alloc_decoder();
string_t tmp;
string_init(tmp);
while(infrared_signal_read(signal, input_file, tmp)) {
ret = false;
if(!infrared_signal_is_valid(signal)) {
printf("Invalid signal\r\n");
break;
}
if(!infrared_signal_is_raw(signal)) {
if(output_file &&
!infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) {
break;
} else {
printf("Skipping decoded signal\r\n");
continue;
}
}
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size);
if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp)))
break;
ret = true;
}
infrared_free_decoder(decoder);
infrared_signal_free(signal);
string_clear(tmp);
return ret;
}
static void infrared_cli_process_decode(Cli* cli, string_t args) {
UNUSED(cli);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
FlipperFormat* output_file = NULL;
uint32_t version;
string_t tmp, header, input_path, output_path;
string_init(tmp);
string_init(header);
string_init(input_path);
string_init(output_path);
do {
if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
printf("Wrong arguments.\r\n");
infrared_cli_print_usage();
break;
}
args_read_probably_quoted_string_and_trim(args, output_path);
if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) {
printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!flipper_format_read_header(input_file, header, &version) ||
(!string_start_with_str_p(header, "IR")) || version != 1) {
printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!string_empty_p(output_path)) {
printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path));
output_file = flipper_format_file_alloc(storage);
}
if(output_file &&
!flipper_format_file_open_always(output_file, string_get_cstr(output_path))) {
printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(output_file && !flipper_format_write_header(output_file, header, version)) {
printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(!infrared_cli_decode_file(input_file, output_file)) {
break;
}
printf("File successfully decoded.\r\n");
} while(false);
string_clear(tmp);
string_clear(header);
string_clear(input_path);
string_clear(output_path);
flipper_format_free(input_file);
if(output_file) flipper_format_free(output_file);
furi_record_close(RECORD_STORAGE);
}
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
UNUSED(context);
if(furi_hal_infrared_is_busy()) {
@@ -169,18 +328,15 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
return;
}
string_t command;
string_init(command);
args_read_string_and_trim(args, command);
size_t i = 0;
for(; i < COUNT_OF(infrared_cli_commands); ++i) {
size_t size = strlen(infrared_cli_commands[i].cmd);
bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size);
if(cmd_found) {
if(string_size(args) == size) {
break;
}
if(string_get_cstr(args)[size] == ' ') {
string_right(args, size + 1);
break;
}
size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
break;
}
}
@@ -189,6 +345,8 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
} else {
infrared_cli_print_usage();
}
string_clear(command);
}
void infrared_on_system_start() {
#ifdef SRV_CLI

View File

@@ -33,6 +33,8 @@ static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
void infrared_scene_universal_common_on_enter(void* context) {
Infrared* infrared = context;
infrared_brute_force_clear_records(infrared->brute_force);
button_panel_reset_selection(infrared->button_panel);
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
}

View File

@@ -16,6 +16,7 @@ ADD_SCENE(infrared, remote_list, RemoteList)
ADD_SCENE(infrared, universal, Universal)
ADD_SCENE(infrared, universal_tv, UniversalTV)
ADD_SCENE(infrared, universal_ac, UniversalAC)
ADD_SCENE(infrared, universal_fan, UniversalFan)
ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, debug, Debug)

View File

@@ -25,7 +25,6 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeSignalReceived) {
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
consumed = true;
@@ -38,6 +37,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
void infrared_scene_learn_on_exit(void* context) {
Infrared* infrared = context;
Popup* popup = infrared->popup;
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
infrared_worker_rx_stop(infrared->worker);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
popup_set_icon(popup, 0, 0, NULL);

View File

@@ -4,6 +4,7 @@ typedef enum {
SubmenuIndexUniversalTV,
SubmenuIndexUniversalAudio,
SubmenuIndexUniversalProjector,
SubmenuIndexUniversalFan,
SubmenuIndexUniversalAirConditioner,
} SubmenuIndex;
@@ -38,6 +39,13 @@ void infrared_scene_universal_on_enter(void* context) {
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"Fans",
SubmenuIndexUniversalFan,
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"ACs",
@@ -63,6 +71,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexUniversalProjector) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);
consumed = true;
} else if(event.event == SubmenuIndexUniversalFan) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalFan);
consumed = true;
} else if(event.event == SubmenuIndexUniversalAirConditioner) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
consumed = true;

View File

@@ -12,7 +12,7 @@ void infrared_scene_universal_ac_on_enter(void* context) {
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/ac.ir"));
//TODO Improve A/C universal remote
button_panel_reserve(button_panel, 2, 2);
button_panel_reserve(button_panel, 2, 3);
uint32_t i = 0;
button_panel_add_item(
button_panel,
@@ -44,7 +44,7 @@ void infrared_scene_universal_ac_on_enter(void* context) {
0,
1,
3,
69,
66,
&I_Vol_up_25x27,
&I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback,
@@ -56,15 +56,39 @@ void infrared_scene_universal_ac_on_enter(void* context) {
1,
1,
36,
69,
66,
&I_Vol_down_25x27,
&I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "TEMP-");
button_panel_add_item(
button_panel,
i,
0,
2,
3,
98,
&I_Swing_25x27,
&I_Swing_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "SWING");
button_panel_add_item(
button_panel,
i,
1,
2,
36,
98,
&I_Timer_25x27,
&I_Timer_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "TIMER");
button_panel_add_label(button_panel, 6, 11, FontPrimary, "AC remote");
button_panel_add_label(button_panel, 20, 66, FontSecondary, "Temp");
button_panel_add_label(button_panel, 20, 63, FontSecondary, "Temp");
button_panel_add_label(button_panel, 8, 23, FontSecondary, "Pwr");
button_panel_add_label(button_panel, 40, 23, FontSecondary, "Mod");

View File

@@ -0,0 +1,113 @@
#include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_fan_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context);
Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force;
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/fans.ir"));
//TODO Improve Fan universal remote
button_panel_reserve(button_panel, 2, 3);
uint32_t i = 0;
button_panel_add_item(
button_panel,
i,
0,
0,
3,
24,
&I_Power_25x27,
&I_Power_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "POWER");
button_panel_add_item(
button_panel,
i,
1,
0,
36,
24,
&I_Mode_25x27,
&I_Mode_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "MODE");
button_panel_add_item(
button_panel,
i,
0,
1,
3,
66,
&I_Vol_up_25x27,
&I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "SPEED+");
button_panel_add_item(
button_panel,
i,
1,
1,
36,
66,
&I_Vol_down_25x27,
&I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "SPEED-");
button_panel_add_item(
button_panel,
i,
0,
2,
3,
98,
&I_Rotate_25x27,
&I_Rotate_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "ROTATE");
button_panel_add_item(
button_panel,
i,
1,
2,
36,
98,
&I_Timer_25x27,
&I_Timer_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "TIMER");
button_panel_add_label(button_panel, 5, 11, FontPrimary, "Fan remote");
button_panel_add_label(button_panel, 20, 63, FontSecondary, "Speed");
button_panel_add_label(button_panel, 8, 23, FontSecondary, "Pwr");
button_panel_add_label(button_panel, 40, 23, FontSecondary, "Mod");
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
infrared_show_loading_popup(infrared, true);
bool success = infrared_brute_force_calculate_messages(brute_force);
infrared_show_loading_popup(infrared, false);
if(!success) {
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
}
}
bool infrared_scene_universal_fan_on_event(void* context, SceneManagerEvent event) {
return infrared_scene_universal_common_on_event(context, event);
}
void infrared_scene_universal_fan_on_exit(void* context) {
infrared_scene_universal_common_on_exit(context);
}

View File

@@ -9,7 +9,7 @@ typedef enum {
void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) {
auto submenu = app->view_controller.get<SubmenuVM>();
submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app);
submenu->add_item("Read ASK (FDX,Regular)", SubmenuASK, submenu_callback, app);
submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {

View File

@@ -4,11 +4,18 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) {
auto submenu = app->view_controller.get<SubmenuVM>();
for(uint8_t i = 0; i < keys_count; i++) {
string_init_printf(
submenu_name[i],
"%s %s",
protocol_dict_get_manufacturer(app->dict, i),
protocol_dict_get_name(app->dict, i));
if(strcmp(
protocol_dict_get_manufacturer(app->dict, i),
protocol_dict_get_name(app->dict, i)) &&
strcmp(protocol_dict_get_manufacturer(app->dict, i), "N/A")) {
string_init_printf(
submenu_name[i],
"%s %s",
protocol_dict_get_manufacturer(app->dict, i),
protocol_dict_get_name(app->dict, i));
} else {
string_init_printf(submenu_name[i], "%s", protocol_dict_get_name(app->dict, i));
}
submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app);
}

View File

@@ -35,6 +35,11 @@ void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) {
popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
app->view_controller.switch_to<PopupVM>();
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
app->old_key_data = (uint8_t*)malloc(size);
protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_write_start(
app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app);
@@ -76,4 +81,8 @@ void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) {
app->view_controller.get<PopupVM>()->clean();
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
free(app->old_key_data);
}

View File

@@ -3,7 +3,7 @@ App(
name="Basic services",
apptype=FlipperAppType.METAPACKAGE,
provides=[
"rpc",
"rpc_start",
"bt",
"desktop",
"loader",
@@ -48,6 +48,7 @@ App(
"tetris_game",
"arkanoid_game",
"tictactoe_game",
"wav_player",
],
)
@@ -75,7 +76,7 @@ App(
"wifi_marauder",
"esp8266_deauth",
"wifi_scanner",
"wav_player",
"multi_converter",
"flipfrid",
],
)

View File

@@ -8,152 +8,158 @@
#include "multi_converter_mode_select.h"
static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
if(multi_converter_state == NULL) {
return;
}
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
if(multi_converter_state == NULL) {
return;
}
if(multi_converter_state->mode == ModeDisplay) {
multi_converter_mode_display_draw(canvas, multi_converter_state);
} else {
multi_converter_mode_select_draw(canvas, multi_converter_state);
}
if (multi_converter_state->mode == ModeDisplay) {
multi_converter_mode_display_draw(canvas, multi_converter_state);
} else {
multi_converter_mode_select_draw(canvas, multi_converter_state);
}
release_mutex((ValueMutex*)ctx, multi_converter_state);
release_mutex((ValueMutex*)ctx, multi_converter_state);
}
static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
static void
multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void multi_converter_init(MultiConverterState* const multi_converter_state) {
// initial default values
// initial default values
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
multi_converter_state->unit_type_orig = UnitTypeDec;
multi_converter_state->unit_type_dest = UnitTypeHex;
multi_converter_state->unit_type_orig = UnitTypeDec;
multi_converter_state->unit_type_dest = UnitTypeHex;
multi_converter_state->keyboard_lock = 0;
multi_converter_state->keyboard_lock = 0;
// init the display view
multi_converter_mode_display_reset(multi_converter_state);
// init the display view
multi_converter_mode_display_reset(multi_converter_state);
// init the select view
multi_converter_mode_select_reset(multi_converter_state);
// init the select view
multi_converter_mode_select_reset(multi_converter_state);
// set ModeDisplay as the current mode
multi_converter_state->mode = ModeDisplay;
// set ModeDisplay as the current mode
multi_converter_state->mode = ModeDisplay;
}
// main entry point
int32_t multi_converter_app(void* p) {
UNUSED(p);
// get event queue
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
// get event queue
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
// allocate state
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
// allocate state
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
// set mutex for plugin state (different threads can access it)
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
free(multi_converter_state);
return 255;
}
// set mutex for plugin state (different threads can access it)
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(multi_converter_state);
return 255;
}
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
multi_converter_init(multi_converter_state);
multi_converter_init(multi_converter_state);
// main loop
MultiConverterEvent event;
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex);
// main loop
MultiConverterEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
MultiConverterState* multi_converter_state =
(MultiConverterState*)acquire_mutex_block(&state_mutex);
if (event_status == FuriStatusOk) {
// press events
if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
if (multi_converter_state->mode == ModeDisplay) {
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
if(multi_converter_state->mode == ModeDisplay) {
if(event.input.key == InputKeyBack) {
if(event.input.type == InputTypePress) processing = false;
} else if(event.input.key == InputKeyOk) { // the "ok" press can be short or long
MultiConverterModeTrigger t = None;
if (event.input.key == InputKeyBack) {
if (event.input.type == InputTypePress) processing = false;
} else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long
MultiConverterModeTrigger t = None;
if(event.input.type == InputTypeLong)
t = multi_converter_mode_display_ok(1, multi_converter_state);
else if(event.input.type == InputTypeShort)
t = multi_converter_mode_display_ok(0, multi_converter_state);
if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state);
else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state);
if(t == Reset) {
multi_converter_mode_select_reset(multi_converter_state);
multi_converter_state->mode = ModeSelector;
}
} else {
if(event.input.type == InputTypePress)
multi_converter_mode_display_navigation(
event.input.key, multi_converter_state);
}
if (t == Reset) {
multi_converter_mode_select_reset(multi_converter_state);
multi_converter_state->mode = ModeSelector;
}
} else {
if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state);
}
} else { // ModeSelect
if (event.input.type == InputTypePress) {
switch (event.input.key) {
default:
break;
case InputKeyBack:
case InputKeyOk: {
MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
} else { // ModeSelect
if(event.input.type == InputTypePress) {
switch(event.input.key) {
default:
break;
case InputKeyBack:
case InputKeyOk: {
MultiConverterModeTrigger t = multi_converter_mode_select_exit(
event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
if (t == Reset) {
multi_converter_mode_display_reset(multi_converter_state);
} else if (t == Convert) {
multi_converter_mode_display_convert(multi_converter_state);
}
if(t == Reset) {
multi_converter_mode_display_reset(multi_converter_state);
} else if(t == Convert) {
multi_converter_mode_display_convert(multi_converter_state);
}
multi_converter_state->keyboard_lock = 1;
multi_converter_state->mode = ModeDisplay;
break;
}
case InputKeyLeft:
case InputKeyRight:
multi_converter_mode_select_switch(multi_converter_state);
break;
case InputKeyUp:
multi_converter_mode_select_change_unit(-1, multi_converter_state);
break;
case InputKeyDown:
multi_converter_mode_select_change_unit(1, multi_converter_state);
break;
}
}
}
} else if (multi_converter_state->keyboard_lock) {
multi_converter_state->keyboard_lock = 0;
}
} else {
// event timeout
}
view_port_update(view_port);
release_mutex(&state_mutex, multi_converter_state);
}
multi_converter_state->keyboard_lock = 1;
multi_converter_state->mode = ModeDisplay;
break;
}
case InputKeyLeft:
case InputKeyRight:
multi_converter_mode_select_switch(multi_converter_state);
break;
case InputKeyUp:
multi_converter_mode_select_change_unit(-1, multi_converter_state);
break;
case InputKeyDown:
multi_converter_mode_select_change_unit(1, multi_converter_state);
break;
}
}
}
} else if(multi_converter_state->keyboard_lock) {
multi_converter_state->keyboard_lock = 0;
}
} else {
// event timeout
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
view_port_update(view_port);
release_mutex(&state_mutex, multi_converter_state);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
free(multi_converter_state);

View File

@@ -1,82 +1,82 @@
#pragma once
#define MULTI_CONVERTER_NUMBER_DIGITS 9
#define MULTI_CONVERTER_NUMBER_DIGITS 9
typedef enum {
EventTypeKey,
EventTypeKey,
} EventType;
typedef struct {
InputEvent input;
EventType type;
InputEvent input;
EventType type;
} MultiConverterEvent;
typedef enum {
ModeDisplay,
ModeSelector,
ModeDisplay,
ModeSelector,
} MultiConverterMode;
typedef enum {
None,
Reset,
Convert,
None,
Reset,
Convert,
} MultiConverterModeTrigger;
// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h)
typedef enum {
UnitTypeDec,
UnitTypeHex,
UnitTypeBin,
UnitTypeDec,
UnitTypeHex,
UnitTypeBin,
UnitTypeCelsius,
UnitTypeFahernheit,
UnitTypeKelvin,
UnitTypeCelsius,
UnitTypeFahernheit,
UnitTypeKelvin,
UnitTypeKilometers,
UnitTypeMeters,
UnitTypeCentimeters,
UnitTypeMiles,
UnitTypeFeet,
UnitTypeInches,
UnitTypeKilometers,
UnitTypeMeters,
UnitTypeCentimeters,
UnitTypeMiles,
UnitTypeFeet,
UnitTypeInches,
UnitTypeDegree,
UnitTypeRadian,
UnitTypeDegree,
UnitTypeRadian,
} MultiConverterUnitType;
typedef struct {
MultiConverterUnitType selected_unit_type_orig;
MultiConverterUnitType selected_unit_type_dest;
uint8_t select_orig;
MultiConverterUnitType selected_unit_type_orig;
MultiConverterUnitType selected_unit_type_dest;
uint8_t select_orig;
} MultiConverterModeSelect;
typedef struct {
uint8_t cursor; // cursor position when typing
int8_t key; // hover key
uint8_t comma; // comma already added? (only one comma allowed)
uint8_t negative; // is negative?
uint8_t cursor; // cursor position when typing
int8_t key; // hover key
uint8_t comma; // comma already added? (only one comma allowed)
uint8_t negative; // is negative?
} MultiConverterModeDisplay;
typedef struct MultiConverterUnit MultiConverterUnit;
typedef struct MultiConverterState MultiConverterState;
struct MultiConverterUnit {
uint8_t allow_comma;
uint8_t allow_negative;
uint8_t max_number_keys;
char mini_name[4];
char name[12];
void (*convert_function)(MultiConverterState * const);
uint8_t (*allowed_function)(MultiConverterUnitType);
uint8_t allow_comma;
uint8_t allow_negative;
uint8_t max_number_keys;
char mini_name[4];
char name[12];
void (*convert_function)(MultiConverterState* const);
uint8_t (*allowed_function)(MultiConverterUnitType);
};
struct MultiConverterState {
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
MultiConverterUnitType unit_type_orig;
MultiConverterUnitType unit_type_dest;
MultiConverterMode mode;
MultiConverterModeDisplay display;
MultiConverterModeSelect select;
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
MultiConverterUnitType unit_type_orig;
MultiConverterUnitType unit_type_dest;
MultiConverterMode mode;
MultiConverterModeDisplay display;
MultiConverterModeSelect select;
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
};

View File

@@ -1,284 +1,326 @@
#include "multi_converter_mode_display.h"
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) {
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
// just copy buffer_orig to buffer_dest and that's it
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
// just copy buffer_orig to buffer_dest and that's it
if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS);
return;
}
if(multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
memcpy(
multi_converter_state->buffer_dest,
multi_converter_state->buffer_orig,
MULTI_CONVERTER_NUMBER_DIGITS);
return;
}
// 2.- origin_buffer has not null functions
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return;
// 2.- origin_buffer has not null functions
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL ||
multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL)
return;
// 3.- valid destination type (using allowed_destinations function)
if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return;
multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state);
// 3.- valid destination type (using allowed_destinations function)
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig)
.allowed_function(multi_converter_state->unit_type_dest))
return;
multi_converter_get_unit(multi_converter_state->unit_type_orig)
.convert_function(multi_converter_state);
}
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
canvas_set_color(canvas, ColorBlack);
void multi_converter_mode_display_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
canvas_set_color(canvas, ColorBlack);
// ORIGIN
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
// ORIGIN
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(
canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
// DESTINATION
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
// DESTINATION
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(
canvas,
2,
10 + 12,
multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
// SEPARATOR_LINE
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
// SEPARATOR_LINE
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
// KEYBOARD
uint8_t _x = 5;
uint8_t _y = 25 + 15; // line + 10
// KEYBOARD
uint8_t _x = 5;
uint8_t _y = 25 + 15; // line + 10
for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
for(int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
char g;
if(i < 10)
g = (i + '0');
else if(i < 16)
g = ((i - 10) + 'A');
else if(i == MULTI_CONVERTER_DISPLAY_KEY_DEL)
g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
else
g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
char g;
if (i < 10) g = (i + '0');
else if (i < 16) g = ((i - 10) + 'A');
else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
uint8_t g_w = canvas_glyph_width(canvas, g);
uint8_t g_w = canvas_glyph_width(canvas, g);
if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
if(i < 16 &&
i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
1) {
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// currently hover key is highlighted
if ((multi_converter_state->display).key == i) {
canvas_draw_box(canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_frame(canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
);
}
// currently hover key is highlighted
if((multi_converter_state->display).key == i) {
canvas_draw_box(
canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_frame(
canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
}
// draw key
canvas_draw_glyph(canvas, _x, _y, g);
// draw key
canvas_draw_glyph(canvas, _x, _y, g);
// certain keys have long_press features, draw whatever they're using there too
if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
canvas_draw_box(canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
4,
2
);
} else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
canvas_draw_box(canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
2,
2
);
}
// certain keys have long_press features, draw whatever they're using there too
if(i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
canvas_draw_box(
canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
4,
2);
} else if(i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
canvas_draw_box(
canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
2,
2);
}
// back to black
canvas_set_color(canvas, ColorBlack);
if (i < 8) {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
} else if (i == 8) {
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3;
_x = 8; // some padding at the beginning on second line
} else {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
}
}
// back to black
canvas_set_color(canvas, ColorBlack);
if(i < 8) {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
} else if(i == 8) {
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) +
3;
_x = 8; // some padding at the beginning on second line
} else {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
}
}
}
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) {
void multi_converter_mode_display_navigation(
InputKey key,
MultiConverterState* const multi_converter_state) {
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
switch(key) {
default:
break;
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
switch (key) {
case InputKeyUp:
case InputKeyDown:
if((multi_converter_state->display).key >= 9)
(multi_converter_state->display).key -= 9;
else
(multi_converter_state->display).key += 9;
break;
default:
break;
case InputKeyLeft:
case InputKeyRight:
case InputKeyUp:
case InputKeyDown:
if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9;
else (multi_converter_state->display).key += 9;
break;
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
case InputKeyLeft:
case InputKeyRight:
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
(multi_converter_state->display).key = 0;
else if((multi_converter_state->display).key < 0)
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
break;
}
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
// if destination key is disabled by max_number_keys, move to the closest one
// (this could be improved with more accurate keys movements, probably...)
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16)
return; // weird, since this means "do not show any number on the keyboard, but just in case..."
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
break;
}
// if destination key is disabled by max_number_keys, move to the closest one
// (this could be improved with more accurate keys movements, probably...)
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..."
int8_t i = -1;
if (key == InputKeyRight || key == InputKeyDown) i = 1;
while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
(multi_converter_state->display).key += i;
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
}
int8_t i = -1;
if(key == InputKeyRight || key == InputKeyDown) i = 1;
while((multi_converter_state->display).key < 16 &&
(multi_converter_state->display).key >
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
1) {
(multi_converter_state->display).key += i;
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
(multi_converter_state->display).key = 0;
else if((multi_converter_state->display).key < 0)
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
}
}
void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) {
// clean the buffers
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// clean the buffers
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// reset the display flags and index
multi_converter_state->display.cursor = 0;
multi_converter_state->display.key = 0;
multi_converter_state->display.comma = 0;
multi_converter_state->display.negative = 0;
// reset the display flags and index
multi_converter_state->display.cursor = 0;
multi_converter_state->display.key = 0;
multi_converter_state->display.comma = 0;
multi_converter_state->display.negative = 0;
}
void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) {
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
if (!(multi_converter_state->display).negative) {
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) {
// we could avoid the blanks, but nevermind
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1];
}
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
void multi_converter_mode_display_toggle_negative(
MultiConverterState* const multi_converter_state) {
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
if(!(multi_converter_state->display).negative) {
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
for(int i = MULTI_CONVERTER_NUMBER_DIGITS - 1; i > 0; i--) {
// we could avoid the blanks, but nevermind
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i - 1];
}
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
// only increment cursor if we're not out of bound
if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++;
} else {
// shift origin buffer one to left, append ' ' on the end
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) {
if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break;
// only increment cursor if we're not out of bound
if((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS)
(multi_converter_state->display).cursor++;
} else {
// shift origin buffer one to left, append ' ' on the end
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS - 1; i++) {
if(multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK)
break;
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1];
}
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i + 1];
}
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS - 1] =
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
(multi_converter_state->display).cursor--;
}
(multi_converter_state->display).cursor--;
}
// toggle flag
(multi_converter_state->display).negative ^= 1;
}
// toggle flag
(multi_converter_state->display).negative ^= 1;
}
}
void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) {
if (
!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
(multi_converter_state->display).comma ||
!(multi_converter_state->display).cursor ||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1))
) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
(multi_converter_state->display).comma || !(multi_converter_state->display).cursor ||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)))
return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
// set flag to one
(multi_converter_state->display).comma = 1;
// set flag to one
(multi_converter_state->display).comma = 1;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
(multi_converter_state->display).cursor++;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
(multi_converter_state->display).cursor++;
}
void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) {
if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return;
if((multi_converter_state->display).key >
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - 1)
return;
if ((multi_converter_state->display).key < 10) {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0';
} else {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A';
}
if((multi_converter_state->display).key < 10) {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
(multi_converter_state->display).key + '0';
} else {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
((multi_converter_state->display).key - 10) + 'A';
}
(multi_converter_state->display).cursor++;
(multi_converter_state->display).cursor++;
}
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) {
MultiConverterModeTrigger multi_converter_mode_display_ok(
uint8_t long_press,
MultiConverterState* const multi_converter_state) {
if((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS)
return None; // limit reached, ignore
if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
if(long_press) {
if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
// toggle negative
multi_converter_mode_display_toggle_negative(multi_converter_state);
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
// add comma
multi_converter_mode_display_add_comma(multi_converter_state);
}
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
if (long_press) {
} else {
// regular keys
multi_converter_mode_display_add_number(multi_converter_state);
}
if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
// toggle negative
multi_converter_mode_display_toggle_negative(multi_converter_state);
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
// add comma
multi_converter_mode_display_add_comma(multi_converter_state);
}
multi_converter_mode_display_convert(multi_converter_state);
} else {
// regular keys
multi_converter_mode_display_add_number(multi_converter_state);
}
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
multi_converter_mode_display_convert(multi_converter_state);
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
MULTI_CONVERTER_DISPLAY_CHAR_COMMA)
(multi_converter_state->display).comma = 0;
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE)
(multi_converter_state->display).negative = 0;
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0;
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0;
multi_converter_mode_display_convert(multi_converter_state);
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_mode_display_convert(multi_converter_state);
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
return Reset;
}
return None;
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
return Reset;
}
return None;
}

View File

@@ -16,12 +16,16 @@ void multi_converter_mode_display_convert(MultiConverterState* const multi_conve
//
// draw the main DISPLAY view with the current multi_converter_state values
//
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_display_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
//
// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down)
//
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state);
void multi_converter_mode_display_navigation(
InputKey key,
MultiConverterState* const multi_converter_state);
//
// reset the DISPLAY mode with the current units, cleaning the buffers and different flags;
@@ -52,4 +56,6 @@ void multi_converter_mode_display_add_number(MultiConverterState* const multi_co
// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...)
// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here)
//
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state);
MultiConverterModeTrigger multi_converter_mode_display_ok(
uint8_t long_press,
MultiConverterState* const multi_converter_state);

View File

@@ -1,160 +1,210 @@
#include "multi_converter_mode_select.h"
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) {
int i = 1;
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d);
if (
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) &&
(multi_converter_state->select).selected_unit_type_orig != ut
) {
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
break;
}
i++;
}
void multi_converter_mode_select_draw_destination_offset(
uint8_t x,
uint8_t y,
int8_t d,
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
int i = 1;
while(
i <
MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
int ut = multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_dest, i * d);
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
.allowed_function(ut) &&
(multi_converter_state->select).selected_unit_type_orig != ut) {
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
break;
}
i++;
}
}
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) {
canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
canvas_set_color(canvas, ColorBlack);
void multi_converter_mode_select_draw_selected_unit(
uint8_t x,
uint8_t y,
MultiConverterUnitType unit_type,
Canvas* const canvas) {
canvas_draw_box(
canvas,
x - 2,
y - 10,
canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4,
13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
canvas_set_color(canvas, ColorBlack);
}
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
void multi_converter_mode_select_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
int y = 10;
int x = 10;
int y = 10;
int x = 10;
canvas_set_color(canvas, ColorBlack);
canvas_set_color(canvas, ColorBlack);
// FROM
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
// FROM
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
canvas_set_font(canvas, FontSecondary);
canvas_set_font(canvas, FontSecondary);
// offset -1
y += 12;
// offset -1
y += 12;
canvas_draw_str(
canvas,
x,
y,
multi_converter_available_units[multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig,
-1)]
.name);
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name);
// current selected element
y += 12;
// current selected element
y += 12;
multi_converter_mode_select_draw_selected_unit(
x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
if((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
// offset +1
y += 12;
// offset +1
y += 12;
canvas_draw_str(
canvas,
x,
y,
multi_converter_available_units[multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig,
1)]
.name);
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name);
// TO
y = 10;
x = 70;
// TO
y = 10;
x = 70;
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
canvas_set_font(canvas, FontSecondary);
canvas_set_font(canvas, FontSecondary);
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
y += 12;
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
y += 12;
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
// current selected element
y += 12;
// current selected element
y += 12;
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
multi_converter_mode_select_draw_selected_unit(
x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
if(!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
// offset +1: same but on the opposite direction
y += 12;
// offset +1: same but on the opposite direction
y += 12;
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
// OK / CANCEL
// OK / CANCEL
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12);
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(
canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
canvas_draw_box(
canvas,
128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
64 - 12,
canvas_string_width(canvas, "BACK: Cancel") + 4,
12);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
canvas_draw_str(
canvas,
128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
64 - 3,
MULTI_CONVERTER_INFO_STRING_BACK);
}
void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) {
// initial pre-selected values are equal to the current selected values
(multi_converter_state->select).selected_unit_type_orig =
multi_converter_state->unit_type_orig;
(multi_converter_state->select).selected_unit_type_dest =
multi_converter_state->unit_type_dest;
// initial pre-selected values are equal to the current selected values
(multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig;
(multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest;
(multi_converter_state->select).select_orig = 1;
(multi_converter_state->select).select_orig = 1;
}
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) {
if (save_changes) {
MultiConverterModeTrigger multi_converter_mode_select_exit(
uint8_t save_changes,
MultiConverterState* const multi_converter_state) {
if(save_changes) {
multi_converter_state->unit_type_dest =
(multi_converter_state->select).selected_unit_type_dest;
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
if(multi_converter_state->unit_type_orig ==
(multi_converter_state->select).selected_unit_type_orig) {
// if the ORIGIN unit didn't changed, just trigger the convert
if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) {
// if the ORIGIN unit didn't changed, just trigger the convert
return Convert;
} else {
multi_converter_state->unit_type_orig =
(multi_converter_state->select).selected_unit_type_orig;
multi_converter_state->unit_type_dest =
(multi_converter_state->select).selected_unit_type_dest;
return Convert;
} else {
multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig;
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
return Reset;
}
}
return Reset;
}
}
return None;
return None;
}
void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) {
(multi_converter_state->select).select_orig ^= 1;
(multi_converter_state->select).select_orig ^= 1;
}
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) {
void multi_converter_mode_select_change_unit(
int8_t direction,
MultiConverterState* const multi_converter_state) {
MultiConverterUnitType d;
if((multi_converter_state->select).select_orig) {
(multi_converter_state->select).selected_unit_type_orig =
multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig, direction);
d = (multi_converter_state->select).selected_unit_type_dest;
} else {
d = ((multi_converter_state->select).selected_unit_type_dest + direction) %
MULTI_CONVERTER_AVAILABLE_UNITS;
}
MultiConverterUnitType d;
if ((multi_converter_state->select).select_orig) {
(multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction);
d = (multi_converter_state->select).selected_unit_type_dest;
} else {
d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS;
}
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
// also notice that ORIGIN must be DIFFERENT than DESTINATION
int i = 0;
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) {
if (
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) &&
(multi_converter_state->select).selected_unit_type_orig != d
) {
(multi_converter_state->select).selected_unit_type_dest = d;
break;
}
d = multi_converter_get_unit_type_offset(d, direction);
i++;
}
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
// also notice that ORIGIN must be DIFFERENT than DESTINATION
int i = 0;
while(i < MULTI_CONVERTER_AVAILABLE_UNITS) {
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
.allowed_function(d) &&
(multi_converter_state->select).selected_unit_type_orig != d) {
(multi_converter_state->select).selected_unit_type_dest = d;
break;
}
d = multi_converter_get_unit_type_offset(d, direction);
i++;
}
}

View File

@@ -10,14 +10,25 @@
//
// aux draw function for units offsets and draw stuff
//
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw_destination_offset(
uint8_t x,
uint8_t y,
int8_t d,
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas);
void multi_converter_mode_select_draw_selected_unit(
uint8_t x,
uint8_t y,
MultiConverterUnitType unit_type,
Canvas* const canvas);
//
// draw the main SELECT view with the current multi_converter_state values
//
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
//
// reset the SELECT mode view, showing as "pre-selected" the current working units
@@ -33,11 +44,13 @@ void multi_converter_mode_select_reset(MultiConverterState* const multi_converte
// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values)
//
// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display
// convert method or reseting the whole display mode (when fully changing the units)
// convert method or reseting the whole display mode (when fully changing the units)
//
// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element
//
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state);
MultiConverterModeTrigger multi_converter_mode_select_exit(
uint8_t save_changes,
MultiConverterState* const multi_converter_state);
//
// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only
@@ -48,11 +61,13 @@ void multi_converter_mode_select_switch(MultiConverterState* const multi_convert
//
// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the
// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param
//
//
// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not
// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to
// properly set a valid destination unit.
//
// (notice the draw step also perform which units are valid to display, so no worries about that here)
//
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state);
void multi_converter_mode_select_change_unit(
int8_t direction,
MultiConverterState* const multi_converter_state);

View File

@@ -1,124 +1,126 @@
#include "multi_converter_units.h"
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
#define multi_converter_unit_set_overflow(b) \
for(int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) \
b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
//
// DEC / HEX / BIN conversion
//
//
void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) {
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
int i = 0;
uint8_t overflow = 0;
int i = 0;
uint8_t overflow = 0;
int a = 0;
int r = 0;
uint8_t f = 1;
int a = 0;
int r = 0;
uint8_t f = 1;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDec: {
a = atoi(multi_converter_state->buffer_orig);
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDec: {
a = atoi(multi_converter_state->buffer_orig);
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
break;
}
case UnitTypeHex:
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
break;
}
case UnitTypeHex:
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
break;
case UnitTypeBin:
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
break;
case UnitTypeBin:
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
break;
}
break;
}
while (a > 0) {
r = a % f;
dest[i] = r + (r < 10 ? '0' : ('A' - 10) );
a /= f;
if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
overflow = 1;
break;
}
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
// copy DEST (reversed) to destination and append empty chars at the end
for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i];
else multi_converter_state->buffer_dest[j] = ' ';
}
}
while(a > 0) {
r = a % f;
dest[i] = r + (r < 10 ? '0' : ('A' - 10));
a /= f;
if(i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
overflow = 1;
break;
}
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
// copy DEST (reversed) to destination and append empty chars at the end
for(int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
if(i >= 1)
multi_converter_state->buffer_dest[j] = dest[--i];
else
multi_converter_state->buffer_dest[j] = ' ';
}
}
}
uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
}
//
// CEL / FAR / KEL
//
void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeCelsius:
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// celsius to fahrenheit
a = (a * ((double)1.8)) + 32;
} else { // UnitTypeKelvin
a += ((double)273.15);
}
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeCelsius:
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// celsius to fahrenheit
a = (a * ((double) 1.8)) + 32;
} else { // UnitTypeKelvin
a += ((double) 273.15);
}
break;
case UnitTypeFahernheit:
// fahrenheit to celsius, always
a = (a - 32) / ((double)1.8);
if(multi_converter_state->unit_type_dest == UnitTypeKelvin) {
// if kelvin, add
a += ((double)273.15);
}
break;
case UnitTypeFahernheit:
// fahrenheit to celsius, always
a = (a - 32) / ((double) 1.8);
if (multi_converter_state->unit_type_dest == UnitTypeKelvin) {
// if kelvin, add
a += ((double) 273.15);
}
break;
case UnitTypeKelvin:
// kelvin to celsius, always
a -= ((double)273.15);
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// if fahernheit, convert
a = (a * ((double)1.8)) + 32;
}
break;
case UnitTypeKelvin:
// kelvin to celsius, always
a -= ((double) 273.15);
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// if fahernheit, convert
a = (a * ((double) 1.8)) + 32;
}
break;
}
break;
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin);
return (
unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit ||
unit_type == UnitTypeKelvin);
}
//
@@ -126,73 +128,102 @@ uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_typ
//
void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeKilometers:
if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157);
break;
case UnitTypeMeters:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157);
break;
case UnitTypeCentimeters:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157);
break;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeKilometers:
if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)1000);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)100000);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.6213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)3280.839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)39370.078740157);
break;
case UnitTypeMeters:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a /= ((double)1000);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)100);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.0006213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)3.280839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)39.370078740157);
break;
case UnitTypeCentimeters:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a /= ((double)100000);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a /= ((double)100);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.000006213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)0.03280839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)0.39370078740157);
break;
case UnitTypeMiles:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360);
break;
case UnitTypeFeet:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12);
break;
case UnitTypeInches:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333);
break;
case UnitTypeMiles:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)1.609344);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)1609.344);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)160934.4);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)5280);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)63360);
break;
case UnitTypeFeet:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)0.0003048);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)0.3048);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)30.48);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.000189393939394);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)12);
break;
case UnitTypeInches:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)0.0000254);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)0.0254);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)2.54);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.0000157828282828);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)0.0833333333333);
break;
}
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) {
return (
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters ||
unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches
);
return (
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters ||
unit_type == UnitTypeCentimeters || unit_type == UnitTypeMiles ||
unit_type == UnitTypeFeet || unit_type == UnitTypeInches);
}
//
@@ -200,31 +231,31 @@ uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type)
//
void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDegree:
if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199);
break;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDegree:
if(multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double)0.0174532925199);
break;
case UnitTypeRadian:
if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131);
break;
}
case UnitTypeRadian:
if(multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double)57.2957795131);
break;
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
}

View File

@@ -8,7 +8,9 @@
#define MULTI_CONVERTER_AVAILABLE_UNITS 14
#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type]
#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS)
#define multi_converter_get_unit_type_offset(unit_type, offset) \
(((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % \
MULTI_CONVERTER_AVAILABLE_UNITS)
// the modulo operation will fail with extremely large values on the units array
// DEC / HEX / BIN
@@ -31,41 +33,139 @@ uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type);
// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function
// (setting functions as NULL will cause convert / select options to be ignored)
//
static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_dec = {
0,
0,
10,
"DEC\0",
"Decimal\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_hex = {
0,
0,
16,
"HEX\0",
"Hexadecimal\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_bin = {
0,
0,
2,
"BIN\0",
"Binary\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_cel = {
1,
1,
10,
"CEL\0",
"Celsius\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_far = {
1,
1,
10,
"FAR\0",
"Fahernheit\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_kel = {
1,
1,
10,
"KEL\0",
"Kelvin\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_km = {
1,
0,
10,
"KM\0",
"Kilometers\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_m = {
1,
0,
10,
"M\0",
"Meters\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_cm = {
1,
0,
10,
"CM\0",
"Centimeters\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_mi = {
1,
0,
10,
"MI\0",
"Miles\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_ft = {
1,
0,
10,
"FT\0",
"Feet\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_in = {
1,
0,
10,
" \"\0",
"Inches\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
static const MultiConverterUnit multi_converter_unit_deg = {
1,
0,
10,
"DEG\0",
"Degree\0",
multi_converter_unit_angle_convert,
multi_converter_unit_angle_allowed};
static const MultiConverterUnit multi_converter_unit_rad = {
1,
0,
10,
"RAD\0",
"Radian\0",
multi_converter_unit_angle_convert,
multi_converter_unit_angle_allowed};
// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h)
static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = {
[UnitTypeDec] = multi_converter_unit_dec,
[UnitTypeHex] = multi_converter_unit_hex,
[UnitTypeBin] = multi_converter_unit_bin,
[UnitTypeDec] = multi_converter_unit_dec,
[UnitTypeHex] = multi_converter_unit_hex,
[UnitTypeBin] = multi_converter_unit_bin,
[UnitTypeCelsius] = multi_converter_unit_cel,
[UnitTypeFahernheit] = multi_converter_unit_far,
[UnitTypeKelvin] = multi_converter_unit_kel,
[UnitTypeCelsius] = multi_converter_unit_cel,
[UnitTypeFahernheit] = multi_converter_unit_far,
[UnitTypeKelvin] = multi_converter_unit_kel,
[UnitTypeKilometers] = multi_converter_unit_km,
[UnitTypeMeters] = multi_converter_unit_m,
[UnitTypeCentimeters] = multi_converter_unit_cm,
[UnitTypeMiles] = multi_converter_unit_mi,
[UnitTypeFeet] = multi_converter_unit_ft,
[UnitTypeInches] = multi_converter_unit_in,
[UnitTypeKilometers] = multi_converter_unit_km,
[UnitTypeMeters] = multi_converter_unit_m,
[UnitTypeCentimeters] = multi_converter_unit_cm,
[UnitTypeMiles] = multi_converter_unit_mi,
[UnitTypeFeet] = multi_converter_unit_ft,
[UnitTypeInches] = multi_converter_unit_in,
[UnitTypeDegree] = multi_converter_unit_deg,
[UnitTypeRadian] = multi_converter_unit_rad,
[UnitTypeDegree] = multi_converter_unit_deg,
[UnitTypeRadian] = multi_converter_unit_rad,
};

View File

@@ -1,7 +1,7 @@
App(
appid="music_player",
name="Music Player",
apptype=FlipperAppType.PLUGIN,
apptype=FlipperAppType.GAME,
entry_point="music_player_app",
cdefines=["APP_MUSIC_PLAYER"],
requires=[
@@ -10,7 +10,7 @@ App(
],
provides=["music_player_start"],
stack_size=2 * 1024,
order=20,
order=45,
)
App(

0
applications/nfc/helpers/nfc_emv_parser.c Executable file → Normal file
View File

0
applications/nfc/helpers/nfc_emv_parser.h Executable file → Normal file
View File

View File

@@ -254,7 +254,7 @@ static void
session_register_page = 234;
break;
default:
furi_assert(false);
furi_crash("Unknown MFUL");
break;
}

View File

@@ -94,6 +94,11 @@ Nfc* nfc_alloc() {
view_dispatcher_add_view(
nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack));
// Detect Reader
nfc->detect_reader = detect_reader_alloc();
view_dispatcher_add_view(
nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader));
// Generator
nfc->generator = NULL;
@@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) {
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
dict_attack_free(nfc->dict_attack);
// Detect Reader
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader);
detect_reader_free(nfc->detect_reader);
// Worker
nfc_worker_stop(nfc->worker);
nfc_worker_free(nfc->worker);

0
applications/nfc/nfc_cli.c Executable file → Normal file
View File

View File

@@ -28,6 +28,7 @@
#include <lib/nfc/parsers/nfc_supported_card.h>
#include "views/dict_attack.h"
#include "views/detect_reader.h"
#include <nfc/scenes/nfc_scene.h>
#include <nfc/helpers/nfc_custom_event.h>
@@ -71,6 +72,7 @@ struct Nfc {
TextBox* text_box;
Widget* widget;
DictAttack* dict_attack;
DetectReader* detect_reader;
const NfcGenerator* generator;
};
@@ -85,6 +87,7 @@ typedef enum {
NfcViewTextBox,
NfcViewWidget,
NfcViewDictAttack,
NfcViewDetectReader,
} NfcView;
Nfc* nfc_alloc();

0
applications/nfc/scenes/nfc_scene.c Executable file → Normal file
View File

2
applications/nfc/scenes/nfc_scene_config.h Executable file → Normal file
View File

@@ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc)
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
ADD_SCENE(nfc, retry_confirm, RetryConfirm)
ADD_SCENE(nfc, detect_reader, DetectReader)
ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo)
ADD_SCENE(nfc, mfkey_complete, MfkeyComplete)
ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)

0
applications/nfc/scenes/nfc_scene_delete.c Executable file → Normal file
View File

0
applications/nfc/scenes/nfc_scene_delete_success.c Executable file → Normal file
View File

View File

@@ -1,126 +1,48 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200)
enum {
NfcSceneDetectReaderStateWidget,
NfcSceneDetectReaderStateTextBox,
};
bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
return true;
}
void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_detect_reader_textbox_callback(void* context) {
void nfc_scene_detect_reader_callback(void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
// Add widget with device name or inform that data received
static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) {
Widget* widget = nfc->widget;
widget_reset(widget);
widget_add_icon_element(widget, 0, 14, &I_Reader_detect);
widget_add_string_element(
widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader");
widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating...");
if(data_received) {
widget_add_button_element(
widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc);
}
}
void nfc_scene_detect_reader_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);
FuriHalNfcDevData nfc_params = {
.uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34},
.uid_len = 7,
.atqa = {0x44, 0x00},
.sak = 0x08,
.type = FuriHalNfcTypeA,
};
nfc->dev->dev_data.nfc_data = nfc_params;
// Setup Widget
nfc_scene_detect_reader_widget_config(nfc, false);
// Setup TextBox
TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex);
text_box_set_focus(text_box, TextBoxFocusEnd);
string_reset(nfc->text_box_store);
// Set Widget state and view
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
nfc_worker_start(
nfc->worker,
NfcWorkerStateUidEmulate,
NfcWorkerStateAnalyzeReader,
&nfc->dev->dev_data,
nfc_detect_reader_worker_callback,
nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader);
nfc_blink_start(nfc);
}
bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) {
// Add data button to widget if data is received for the first time
if(!string_size(nfc->text_box_store)) {
nfc_scene_detect_reader_widget_config(nfc, true);
}
// Update TextBox data
if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) {
string_cat_printf(nfc->text_box_store, "R:");
for(uint16_t i = 0; i < reader_data->size; i++) {
string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]);
}
string_push_back(nfc->text_box_store, '\n');
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
}
memset(reader_data, 0, sizeof(NfcReaderRequestData));
if(event.event == NfcCustomEventViewExit) {
nfc_worker_stop(nfc->worker);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo);
consumed = true;
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox);
consumed = true;
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if(state == NfcSceneDetectReaderStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
} else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) {
detect_reader_inc_nonce_cnt(nfc->detect_reader);
consumed = true;
}
}
@@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) {
nfc_worker_stop(nfc->worker);
// Clear view
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store);
detect_reader_reset(nfc->detect_reader);
nfc_blink_stop(nfc);
}

0
applications/nfc/scenes/nfc_scene_emulate_uid.c Executable file → Normal file
View File

0
applications/nfc/scenes/nfc_scene_file_select.c Executable file → Normal file
View File

View File

View File

@@ -0,0 +1,49 @@
#include "../nfc_i.h"
void nfc_scene_mfkey_complete_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mfkey_complete_on_enter(void* context) {
Nfc* nfc = context;
widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!");
widget_add_string_multiline_element(
nfc->widget,
64,
32,
AlignCenter,
AlignCenter,
FontSecondary,
"Now use mfkey32v2\nto extract keys");
widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeCenter) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
} else if(event.event == SceneManagerEventTypeBack) {
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
void nfc_scene_mfkey_complete_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
}

View File

@@ -0,0 +1,55 @@
#include "../nfc_i.h"
#include <lib/nfc/helpers/mfkey32.h>
void nfc_scene_mfkey_nonces_info_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mfkey_nonces_info_on_enter(void* context) {
Nfc* nfc = context;
string_t temp_str;
string_init(temp_str);
uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str);
widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str));
string_printf(temp_str, "Nonces saved %d", nonces_saved);
widget_add_string_element(
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str));
widget_add_string_element(
nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:");
widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc);
string_clear(temp_str);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
void nfc_scene_mfkey_nonces_info_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget);
}

0
applications/nfc/scenes/nfc_scene_read_card_success.c Executable file → Normal file
View File

0
applications/nfc/scenes/nfc_scene_set_atqa.c Executable file → Normal file
View File

0
applications/nfc/scenes/nfc_scene_set_sak.c Executable file → Normal file
View File

0
applications/nfc/scenes/nfc_scene_set_uid.c Executable file → Normal file
View File

View File

@@ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
if(sd_exist) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
}
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
@@ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
consumed = true;
} else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
consumed = true;
}

View File

@@ -0,0 +1,115 @@
#include "detect_reader.h"
#include <gui/elements.h>
struct DetectReader {
View* view;
DetectReaderDoneCallback callback;
void* context;
};
typedef struct {
uint16_t nonces;
} DetectReaderViewModel;
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
DetectReaderViewModel* m = model;
char text[32] = {};
snprintf(text, sizeof(text), "Tap the reader several times");
canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times");
if(m->nonces == 0) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating...");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic");
canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38);
} else {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting...");
canvas_set_font(canvas, FontSecondary);
snprintf(text, sizeof(text), "Nonces: %d", m->nonces);
canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text);
elements_button_right(canvas, "Next");
canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36);
}
}
static bool detect_reader_input_callback(InputEvent* event, void* context) {
DetectReader* detect_reader = context;
furi_assert(detect_reader->callback);
bool consumed = false;
uint8_t nonces = 0;
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
nonces = model->nonces;
return false;
});
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
if(nonces > 0) {
detect_reader->callback(detect_reader->context);
consumed = true;
}
}
}
return consumed;
}
DetectReader* detect_reader_alloc() {
DetectReader* detect_reader = malloc(sizeof(DetectReader));
detect_reader->view = view_alloc();
view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel));
view_set_draw_callback(detect_reader->view, detect_reader_draw_callback);
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
view_set_context(detect_reader->view, detect_reader);
return detect_reader;
}
void detect_reader_free(DetectReader* detect_reader) {
furi_assert(detect_reader);
view_free(detect_reader->view);
free(detect_reader);
}
void detect_reader_reset(DetectReader* detect_reader) {
furi_assert(detect_reader);
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
model->nonces = 0;
return false;
});
}
View* detect_reader_get_view(DetectReader* detect_reader) {
furi_assert(detect_reader);
return detect_reader->view;
}
void detect_reader_set_callback(
DetectReader* detect_reader,
DetectReaderDoneCallback callback,
void* context) {
furi_assert(detect_reader);
furi_assert(callback);
detect_reader->callback = callback;
detect_reader->context = context;
}
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) {
furi_assert(detect_reader);
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
model->nonces++;
return false;
});
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
#include <gui/view.h>
#include <gui/modules/widget.h>
typedef struct DetectReader DetectReader;
typedef void (*DetectReaderDoneCallback)(void* context);
DetectReader* detect_reader_alloc();
void detect_reader_free(DetectReader* detect_reader);
void detect_reader_reset(DetectReader* detect_reader);
View* detect_reader_get_view(DetectReader* detect_reader);
void detect_reader_set_callback(
DetectReader* detect_reader,
DetectReaderDoneCallback callback,
void* context);
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader);

View File

@@ -9,8 +9,10 @@
#include <toolbox/stream/file_stream.h>
#define LOGITECH_MAX_CHANNEL 85
#define COUNT_THRESHOLD 4
#define SAMPLE_TIME 20000
#define COUNT_THRESHOLD 2
#define DEFAULT_SAMPLE_TIME 8000
#define MAX_ADDRS 100
#define MAX_CONFIRMED 32
#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff"
#define NRFSNIFF_APP_FILENAME "addresses.txt"
@@ -32,31 +34,36 @@ typedef struct {
} PluginState;
char rate_text_fmt[] = "Transfer rate: %dMbps";
char sample_text_fmt[] = "Sample Time: %d ms";
char channel_text_fmt[] = "Channel: %d";
char preamble_text_fmt[] = "Preamble: %02X";
char sniff_text_fmt[] = "Sniffing: %s Found: %d";
char addresses_header_text[] = "Address,rate";
char sniffed_address_fmt[] = "%s,%d";
char rate_text[46];
char channel_text[42];
char channel_text[14];
char sample_text[32];
char preamble_text[14];
char sniff_text[38];
char sniffed_address[14];
uint8_t target_channel = 0;
uint32_t found_count = 0;
uint32_t sample_time = DEFAULT_SAMPLE_TIME;
uint8_t target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
uint8_t target_preamble[] = {0xAA, 0x00};
uint8_t sniffing_state = false;
char top_address[12];
uint8_t candidates[100][5] = {0}; // top 100 recurring addresses
uint32_t counts[100];
uint8_t total_candidates = 0;
uint8_t last_cleanup_idx = 101; // avoid replacing the last replaced addr
uint8_t candidates[MAX_ADDRS][5] = {0}; // last 100 sniffed addresses
uint32_t counts[MAX_ADDRS];
uint8_t confirmed[MAX_CONFIRMED][5] = {0}; // first 32 confirmed addresses
uint8_t confirmed_idx = 0;
uint32_t total_candidates = 0;
uint32_t candidate_idx = 0;
static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
for(int i = 0; i < total_candidates; i++) {
for(uint32_t i = 0; i < total_candidates; i++) {
uint8_t* arr_item = candidates[i];
if(!memcmp(arr_item, addr, addr_size)) return i;
}
@@ -64,32 +71,10 @@ static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
return -1;
}
/*
static uint32_t get_addr_count(uint8_t* addr, uint8_t addr_size)
{
return counts[get_addr_index(addr, addr_size)];
}
*/
static uint8_t get_lowest_idx() {
uint32_t lowest = 10000;
uint8_t lowest_idx = 0;
for(uint8_t i = 0; i < total_candidates; i++) {
if(i == last_cleanup_idx) continue;
if(counts[i] < lowest) {
lowest = counts[i];
lowest_idx = i;
}
}
last_cleanup_idx = lowest_idx;
return lowest_idx;
}
static uint8_t get_highest_idx() {
static int get_highest_idx() {
uint32_t highest = 0;
uint8_t highest_idx = 0;
for(uint8_t i = 0; i < total_candidates; i++) {
int highest_idx = 0;
for(uint32_t i = 0; i < total_candidates; i++) {
if(counts[i] > highest) {
highest = counts[i];
highest_idx = i;
@@ -99,15 +84,14 @@ static uint8_t get_highest_idx() {
return highest_idx;
}
// if array is full, start over from beginning
static void insert_addr(uint8_t* addr, uint8_t addr_size) {
uint8_t idx = total_candidates;
if(total_candidates > 99) {
// replace addr with lowest count
idx = get_lowest_idx();
}
memcpy(candidates[idx], addr, addr_size);
counts[idx] = 1;
if(total_candidates < 100) total_candidates++;
if(candidate_idx >= MAX_ADDRS) candidate_idx = 0;
memcpy(candidates[candidate_idx], addr, addr_size);
counts[candidate_idx] = 1;
if(total_candidates < MAX_ADDRS) total_candidates++;
candidate_idx++;
}
static void render_callback(Canvas* const canvas, void* ctx) {
@@ -128,13 +112,15 @@ static void render_callback(Canvas* const canvas, void* ctx) {
snprintf(rate_text, sizeof(rate_text), rate_text_fmt, (int)rate);
snprintf(channel_text, sizeof(channel_text), channel_text_fmt, (int)target_channel);
snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]);
snprintf(sample_text, sizeof(sample_text), sample_text_fmt, (int)sample_time);
//snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]);
snprintf(sniff_text, sizeof(sniff_text), sniff_text_fmt, sniffing, found_count);
snprintf(
sniffed_address, sizeof(sniffed_address), sniffed_address_fmt, top_address, (int)rate);
canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, rate_text);
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, channel_text);
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, sample_text);
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, channel_text);
//canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
canvas_draw_str_aligned(canvas, 10, 40, AlignLeft, AlignBottom, sniff_text);
canvas_draw_str_aligned(canvas, 30, 50, AlignLeft, AlignBottom, addresses_header_text);
canvas_draw_str_aligned(canvas, 30, 60, AlignLeft, AlignBottom, sniffed_address);
@@ -256,12 +242,24 @@ void alt_address(uint8_t* addr, uint8_t* altaddr) {
for(int i = 0; i < 5; i++) altaddr[i] = tmpaddr[4 - i];
}
static bool previously_confirmed(uint8_t* addr) {
bool found = false;
for(int i = 0; i < MAX_CONFIRMED; i++) {
if(!memcmp(confirmed[i], addr, 5)) {
found = true;
break;
}
}
return found;
}
static void wrap_up(Storage* storage, NotificationApp* notification) {
uint8_t ch;
uint8_t addr[5];
uint8_t altaddr[5];
char trying[12];
uint8_t idx;
int idx;
uint8_t rate = 0;
if(target_rate == 8) rate = 2;
@@ -291,14 +289,24 @@ static void wrap_up(Storage* storage, NotificationApp* notification) {
hexlify(addr, 5, top_address);
save_addr_to_file(storage, addr, 5, notification);
found_count++;
if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5);
break;
}
}
}
static void start_sniffing() {
static void clear_cache() {
found_count = 0;
confirmed_idx = 0;
candidate_idx = 0;
target_channel = 2;
total_candidates = 0;
memset(candidates, 0, sizeof(candidates));
memset(counts, 0, sizeof(counts));
memset(confirmed, 0, sizeof(confirmed));
}
static void start_sniffing() {
nrf24_init_promisc_mode(nrf24_HANDLE, target_channel, target_rate);
}
@@ -311,8 +319,8 @@ int32_t nrfsniff_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
FURI_LOG_E(TAG, "cannot create mutex\r\n");
free(plugin_state);
return 255;
}
@@ -366,18 +374,20 @@ int32_t nrfsniff_app(void* p) {
break;
case InputKeyRight:
// increment channel
if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
target_channel++;
//if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
// target_channel++;
sample_time += 500;
break;
case InputKeyLeft:
// decrement channel
if(!sniffing_state && target_channel > 0) target_channel--;
//if(!sniffing_state && target_channel > 0) target_channel--;
if(sample_time > 500) sample_time -= 500;
break;
case InputKeyOk:
// toggle sniffing
sniffing_state = !sniffing_state;
if(sniffing_state) {
found_count = 0;
clear_cache();
start_sniffing();
start = furi_get_tick();
} else
@@ -398,22 +408,26 @@ int32_t nrfsniff_app(void* p) {
if(nrf24_sniff_address(nrf24_HANDLE, 5, address)) {
int idx;
uint8_t* top_addr;
idx = get_addr_index(address, 5);
if(idx == -1)
insert_addr(address, 5);
else
counts[idx]++;
if(!previously_confirmed(address)) {
idx = get_addr_index(address, 5);
if(idx == -1)
insert_addr(address, 5);
else
counts[idx]++;
top_addr = candidates[get_highest_idx()];
hexlify(top_addr, 5, top_address);
top_addr = candidates[get_highest_idx()];
hexlify(top_addr, 5, top_address);
}
}
if(furi_get_tick() - start >= SAMPLE_TIME) {
wrap_up(storage, notification);
if(furi_get_tick() - start >= sample_time) {
target_channel++;
if(target_channel > LOGITECH_MAX_CHANNEL) target_channel = 2;
{
wrap_up(storage, notification);
start_sniffing();
}
start_sniffing();
start = furi_get_tick();
}
}
@@ -422,6 +436,10 @@ int32_t nrfsniff_app(void* p) {
release_mutex(&state_mutex, plugin_state);
}
clear_cache();
sample_time = DEFAULT_SAMPLE_TIME;
target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
sniffing_state = false;
furi_hal_spi_release(nrf24_HANDLE);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
@@ -432,4 +450,4 @@ int32_t nrfsniff_app(void* p) {
furi_message_queue_free(event_queue);
return 0;
}
}

View File

@@ -190,12 +190,87 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
return ERR_NONE;
}
ReturnCode picopass_write_card(PicopassBlock* AA1) {
rfalPicoPassIdentifyRes idRes;
rfalPicoPassSelectRes selRes;
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
ReturnCode err;
uint8_t div_key[8] = {0};
uint8_t mac[4] = {0};
uint8_t ccnr[12] = {0};
err = rfalPicoPassPollerIdentify(&idRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
return err;
}
err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
return err;
}
err = rfalPicoPassPollerReadCheck(&rcRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
return err;
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
return err;
}
for(size_t i = 6; i < 10; i++) {
FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i);
uint8_t data[9] = {0};
data[0] = i;
memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
loclass_doMAC_N(data, sizeof(data), div_key, mac);
FURI_LOG_D(
TAG,
"loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
i,
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
data[8],
mac[0],
mac[1],
mac[2],
mac[3]);
err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
return err;
}
}
return ERR_NONE;
}
int32_t picopass_worker_task(void* context) {
PicopassWorker* picopass_worker = context;
picopass_worker_enable_field();
if(picopass_worker->state == PicopassWorkerStateDetect) {
picopass_worker_detect(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWrite) {
picopass_worker_write(picopass_worker);
}
picopass_worker_disable_field(ERR_NONE);
@@ -212,27 +287,60 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
PicopassPacs* pacs = &dev_data->pacs;
ReturnCode err;
PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
while(picopass_worker->state == PicopassWorkerStateDetect) {
if(picopass_detect_card(1000) == ERR_NONE) {
// Process first found device
err = picopass_read_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
nextState = PicopassWorkerEventFail;
}
err = picopass_device_parse_credential(AA1, pacs);
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_credential(AA1, pacs);
}
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
nextState = PicopassWorkerEventFail;
}
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
if(nextState == PicopassWorkerEventSuccess) {
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
}
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
nextState = PicopassWorkerEventFail;
}
// Notify caller and exit
if(picopass_worker->callback) {
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
picopass_worker->callback(nextState, picopass_worker->context);
}
break;
}
furi_delay_ms(100);
}
}
void picopass_worker_write(PicopassWorker* picopass_worker) {
PicopassDeviceData* dev_data = picopass_worker->dev_data;
PicopassBlock* AA1 = dev_data->AA1;
ReturnCode err;
PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
while(picopass_worker->state == PicopassWorkerStateWrite) {
if(picopass_detect_card(1000) == ERR_NONE) {
err = picopass_write_card(AA1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_write_card error %d", err);
nextState = PicopassWorkerEventFail;
}
// Notify caller and exit
if(picopass_worker->callback) {
picopass_worker->callback(nextState, picopass_worker->context);
}
break;
}

1
applications/picopass/picopass_worker.h Executable file → Normal file
View File

@@ -11,6 +11,7 @@ typedef enum {
PicopassWorkerStateReady,
// Main worker states
PicopassWorkerStateDetect,
PicopassWorkerStateWrite,
// Transition
PicopassWorkerStateStop,
} PicopassWorkerState;

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