From bd743dc1d8ea2c55ba0a838055941b57caf993b1 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 17 Jun 2023 21:50:24 +0300
Subject: [PATCH 01/19] Update changelog and readme
---
CHANGELOG.md | 32 ++++++++++++++++++--------------
ReadMe.md | 10 +++++-----
2 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a03f11808..9f6ac7196 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,9 @@
### New changes
-* BLE: Revert BLE gatt characteristics refactoring temporarily -> **Should fix HID issues on older iOS, and maybe some issues with android app**
-* SubGHz: Added external cc1101 module at CLI (by @Sil333033 & @xMasterX | PR #513)
-* SubGHz: Remove unused global var
-* Plugins: Fix ProtoView issue #503 again -> (Broken saved files with custom modulation)
-* OFW: furi_hal_nfc: fix rfalTransceiveBitsBlockingTx's 4th argument to bits count rather than bytes count
-* OFW: FuriHal: remove clock startup time tracking from clean builds
+* Infrared: Updated universal remote asstes (by @amec0e | PR #522)
+* Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app
+* OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed
-#### [π² Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)
+----
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
@@ -25,19 +22,26 @@
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
-### Thanks to our sponsors:
+#### Thanks to our sponsors:
callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
and all other great people who supported our project and me (xMasterX), thanks to you all!
-**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed**
-**Recommended option - Web Updater**
+### **Recommended update option - Web Updater**
### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater?
-What means `n` or `e` in - `flipper-z-f7-update-(version)(n / r / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations,
-`e` means build has extra apps pack preinstalled,
-`r` means RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!)
+What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ?
+`flipper-z` = for Flipper Zero device
+`f7` = Hardware version - same for all flipper zero devices
+`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself
+`(version)` = Firmware version
+`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations
+`e` = build has π² [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
+`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!)
+
+Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web
+Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`
+SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip`
-Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper / web
diff --git a/ReadMe.md b/ReadMe.md
index 19d74fed8..3ab8e4b4a 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -6,15 +6,15 @@
### Welcome to the Flipper Zero Unleashed Firmware repo!
-**This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
+#### **This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
-Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
+### Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
-### This software is for experimental purposes only and is not meant for any illegal activity/purposes.
We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.
Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
+##### This software is for experimental purposes only and is not meant for any illegal activity/purposes.
We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.
Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
Our Discord Community:
@@ -79,7 +79,7 @@ Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported man
Encoders or sending made by @xMasterX:
- Nero Radio 57bit (+ 56bit encoder improvements)
-- CAME 12bit/24bit encoder fixes (already merged in OFW)
+- CAME 12bit/24bit encoder fixes (Fixes now merged in OFW)
- Keeloq: HCS101
- Keeloq: AN-Motors
- Keeloq: JCM Tech
@@ -97,7 +97,7 @@ Encoders or sending made by @xMasterX:
Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version):
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
-- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
+- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: @mmx7)]
- Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
- Star Line
- Security+ v1 & v2 (encoders was made in OFW)
From 851aabdcb5cfd6bbec510fa759248e8bfb28f91e Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 17 Jun 2023 21:52:27 +0300
Subject: [PATCH 02/19] Update changelog
---
CHANGELOG.md | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f6ac7196..81edf5ec4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-### New changes
+## New changes
* Infrared: Updated universal remote asstes (by @amec0e | PR #522)
* Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app
* OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed
@@ -27,16 +27,16 @@ callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron
and all other great people who supported our project and me (xMasterX), thanks to you all!
-### **Recommended update option - Web Updater**
+## **Recommended update option - Web Updater**
### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater?
-What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ?
-`flipper-z` = for Flipper Zero device
-`f7` = Hardware version - same for all flipper zero devices
-`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself
-`(version)` = Firmware version
-`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations
-`e` = build has π² [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
+What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ?
+`flipper-z` = for Flipper Zero device
+`f7` = Hardware version - same for all flipper zero devices
+`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself
+`(version)` = Firmware version
+`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations
+`e` = build has π² [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!)
Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web
From 1c06ac1a82b083ae8c673b618eb4080c04f58400 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 17 Jun 2023 21:53:03 +0300
Subject: [PATCH 03/19] ...
---
CHANGELOG.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81edf5ec4..e4249c94f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,8 +39,8 @@ What build I should download and what this name means - `flipper-z-f7-update-(ve
`e` = build has π² [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!)
-Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web
-Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`
+Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web
+Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`
SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip`
From 62da715f754b95b6ff0e3cb596b5be4ff5d232ed Mon Sep 17 00:00:00 2001
From: gid9798 <30450294+gid9798@users.noreply.github.com>
Date: Thu, 22 Jun 2023 22:33:01 +0300
Subject: [PATCH 04/19] Hid app: keynote vertical redraw and cleanup
---
.../external/hid_app/assets/Space_60x18.png | Bin 0 -> 2871 bytes
applications/external/hid_app/hid.c | 18 +-
applications/external/hid_app/hid.h | 2 -
applications/external/hid_app/views.h | 1 -
.../external/hid_app/views/hid_keynote.c | 98 ++++++++
.../external/hid_app/views/hid_keynote.h | 2 +
.../hid_app/views/hid_keynote_vertical.c | 228 ------------------
.../hid_app/views/hid_keynote_vertical.h | 16 --
8 files changed, 104 insertions(+), 261 deletions(-)
create mode 100644 applications/external/hid_app/assets/Space_60x18.png
delete mode 100644 applications/external/hid_app/views/hid_keynote_vertical.c
delete mode 100644 applications/external/hid_app/views/hid_keynote_vertical.h
diff --git a/applications/external/hid_app/assets/Space_60x18.png b/applications/external/hid_app/assets/Space_60x18.png
new file mode 100644
index 0000000000000000000000000000000000000000..e29f50ae9220d2f9a9753850dedcc6be0a211e76
GIT binary patch
literal 2871
zcmV-73&`||P)004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv
zX+uL$Nkc;*aB^>EX>4Tx07%E3mUmQC*A|D*y?1({%`nm#dXp|Nfb=dP9RyJrW(F9_
z0K*JTY>22pL=h1IMUbF?0i&TvtcYSED5zi$NDxqBFp8+CWJcCXe0h2A<>mLsz2Dkr
z?{oLrd!Mx~03=TzE-wX^0w9?u;0Jm*(^rK@(6Rjh26%u0rT{Qm>8ZX!?!iDLFE@L0LWj&=4?(nOT_siPRbOditRHZrp6?S8Agej
zFG^6va$=5K|`EW#NwP&*~x4%_lS6VhL9s-#7D#h8C*`Lh;NHnGf9}t
z74chfY%+(L4giWIwhK6{coCb3n8XhbbP@4#0C1$ZFF5847I3lz;zPNlq-OKEaq$AW
zE=!MYYHiJ+dvY?9I0Av8Ka-Wn(gPeepdb@piwLhwjRWWeSr7baCBSDM=|p
zK0Q5^$>Pur|2)M1IPkCYSQ^NQ`z*p
zYmq4Rp8z$=2uR(a0_5jDfT9oq5_wSE_22vEgAWDbn-``!u{igi1^xT3aEbVl&W-yV
z=Mor9X9@Wki)-R*3DAH5Bmou30~MeFbb%o-16IHmI084Y0{DSo5DwM?7KjJQfDbZ3
zF4znTKoQsl_JT@K1L{E|XaOfc2RIEbfXm=IxC!on2Vew@gXdrdyaDqN1YsdEM1kZX
zRY(gmfXpBUWDmJPK2RVO4n;$85DyYUxzHA<2r7jtp<1XB`W89`U4X7a1JFHa6qn9`(3jA6(BtSg7z~Dn
z(ZN_@JTc*z1k5^2G3EfK6>}alfEmNgVzF3xtO3>z>xX4x1=s@Ye(W*qIqV>I9QzhW
z#Hr%UaPGJW91oX=E5|kA&f*4f6S#T26kZE&gZIO;@!9wid_BGke*-^`pC?EYbO?5Y
zU_t_6GogaeLbybDNO(mg64i;;!~i0fxQSRnJWjkq93{RZ$&mC(E~H43khGI@gmj*C
zkMxR6CTo)&$q{4$c_+D%e3AT^{8oY@VI<)t!Is!4Q6EtGo7CCWGzL)D>rQ4^>|)NiQ$)EQYB*=4e!vRSfKvS(yRXb4T4
z=0!`QmC#PmhG_4XC@*nZ!dbFoNz0PKC3A9$a*lEwxk9;CxjS<2<>~Tn@`>`hkG4N#KjNU~z;vi{c;cwx$aZXSoN&@}N^m;n^upQ1neW`@Jm+HLvfkyqE8^^jVTFG14;RpP@{Py@g^4IZC^Zz~o6W||E74S6BG%z=?
zH;57x71R{;CfGT+B=|vyZiq0XJ5(|>GPE&tF3dHoG;Cy*@v8N!u7@jxbHh6$uo0mV
z4H2`e-B#~iJsxQhSr9q2MrTddnyYIS)+Vhz6D1kNj5-;Ojt+}%ivGa#W7aWeW4vOj
zV`f+`tbMHKY)5t(dx~SnDdkMW+QpW}PR7~A?TMR;cZe^KpXR!7E4eQdJQHdX<`Vr9
zk0dT6g(bBnMJ7e%MIVY;#n-+v{i@=tg`KfG`%5fK4(`J2;_VvR?Xdf3
zsdQ;h>DV6MJ?&-mvcj_0d!zPVEnik%vyZS(xNoGwr=oMe=Kfv#KUBt7-l=k~YOPkP
z-cdbwfPG-_pyR=o8s(azn)ipehwj#T)V9}Y*Oec}9L_lWv_7=H_iM)2jSUJ7MGYU1
z@Q#ce4LsV@Xw}%*q|{W>3^xm#r;bG)yZMdlH=QkpEw!z*)}rI!xbXP1Z==5*I^lhy
z`y}IJ%XeDeRku;v3frOf?DmPgz@Xmo#D^7KH*><&kZ}k0<(`u)y&d8oAIZHU3
ze|F(q&bit1spqFJ#9bKcj_Q7Jan;4!Jpn!am%J}sx$J)VVy{#0xhr;8PG7aTdg>bE
zTE}(E>+O9OeQiHj{Lt2K+24M{>PF{H>ziEz%LmR5It*U8<$CM#ZLizc@2tEtFcdO$
zcQ|r*xkvZnNio#z9&IX9*nWZ
zp8u5o(}(f=r{t&Q6RH!9lV+2rr`)G*K3n~4{CVp0`RRh6rGKt|q5I;yUmSnwn^`q8
z{*wQ4;n(6<@~@7(UiP|s)_?Z#o8&k1bA@l^-yVI(c-Q+r?ES=i<_GMDijR69yFPh;
zdbp6hu<#rAg!B8%JG^WF000SaNLh0L01m_e01m_fl`9S#0000PbVXQnQ*UN;cVTj6
z06}DLVr3vnZDD6+Qe|Oed2z{QJOBUyO-V#SR9Hvt&&vq_APfZ2?Z4?5q7fA<73t;DzTElPZdnb+W-vX2=^0GVV0s4AyTEkxc3v0wl(p9E_klFChyj!;
VN_%sSbR7Ty002ovPDHLkV1hy!X)pi)
literal 0
HcmV?d00001
diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c
index d115f5bbf..7b136e63f 100644
--- a/applications/external/hid_app/hid.c
+++ b/applications/external/hid_app/hid.c
@@ -22,10 +22,12 @@ static void hid_submenu_callback(void* context, uint32_t index) {
Hid* app = context;
if(index == HidSubmenuIndexKeynote) {
app->view_id = HidViewKeynote;
+ hid_keynote_set_orientation(app->hid_keynote, false);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeynoteVertical) {
- app->view_id = HidViewKeynoteVertical;
- view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical);
+ app->view_id = HidViewKeynote;
+ hid_keynote_set_orientation(app->hid_keynote, true);
+ view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeyboard) {
app->view_id = HidViewKeyboard;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
@@ -62,7 +64,6 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
}
}
hid_keynote_set_connected_status(hid->hid_keynote, connected);
- hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_numpad_set_connected_status(hid->hid_numpad, connected);
hid_media_set_connected_status(hid->hid_media, connected);
@@ -177,15 +178,6 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view(
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
- // Keynote Vertical view
- app->hid_keynote_vertical = hid_keynote_vertical_alloc(app);
- view_set_previous_callback(
- hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view);
- view_dispatcher_add_view(
- app->view_dispatcher,
- HidViewKeynoteVertical,
- hid_keynote_vertical_get_view(app->hid_keynote_vertical));
-
// Keyboard view
app->hid_keyboard = hid_keyboard_alloc(app);
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
@@ -252,8 +244,6 @@ void hid_free(Hid* app) {
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
hid_keynote_free(app->hid_keynote);
- view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical);
- hid_keynote_vertical_free(app->hid_keynote_vertical);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
hid_keyboard_free(app->hid_keyboard);
view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad);
diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h
index 4a8cd2a98..3899c860a 100644
--- a/applications/external/hid_app/hid.h
+++ b/applications/external/hid_app/hid.h
@@ -17,7 +17,6 @@
#include
#include
#include "views/hid_keynote.h"
-#include "views/hid_keynote_vertical.h"
#include "views/hid_keyboard.h"
#include "views/hid_numpad.h"
#include "views/hid_media.h"
@@ -43,7 +42,6 @@ struct Hid {
Submenu* device_type_submenu;
DialogEx* dialog;
HidKeynote* hid_keynote;
- HidKeynoteVertical* hid_keynote_vertical;
HidKeyboard* hid_keyboard;
HidNumpad* hid_numpad;
HidMedia* hid_media;
diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h
index 5d02220cd..1f1536486 100644
--- a/applications/external/hid_app/views.h
+++ b/applications/external/hid_app/views.h
@@ -1,7 +1,6 @@
typedef enum {
HidViewSubmenu,
HidViewKeynote,
- HidViewKeynoteVertical,
HidViewKeyboard,
HidViewNumpad,
HidViewMedia,
diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c
index 5e5eeb790..543363bf6 100644
--- a/applications/external/hid_app/views/hid_keynote.c
+++ b/applications/external/hid_app/views/hid_keynote.c
@@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
}
+static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {
+ furi_assert(context);
+ HidKeynoteModel* model = context;
+
+ // Header
+ if(model->transport == HidTransportBle) {
+ if(model->connected) {
+ canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
+ } else {
+ canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
+ }
+ canvas_set_font(canvas, FontPrimary);
+ elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
+ } else {
+ canvas_set_font(canvas, FontPrimary);
+ elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
+ }
+
+ canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8);
+ canvas_set_font(canvas, FontSecondary);
+ elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
+
+ const uint8_t x_2 = 23;
+ const uint8_t x_1 = 2;
+ const uint8_t x_3 = 44;
+
+ const uint8_t y_1 = 44;
+ const uint8_t y_2 = 65;
+
+ // Up
+ canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18);
+ if(model->up_pressed) {
+ elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop);
+ canvas_set_color(canvas, ColorBlack);
+
+ // Down
+ canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18);
+ if(model->down_pressed) {
+ elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom);
+ canvas_set_color(canvas, ColorBlack);
+
+ // Left
+ canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18);
+ if(model->left_pressed) {
+ elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft);
+ canvas_set_color(canvas, ColorBlack);
+
+ // Right
+ canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18);
+ if(model->right_pressed) {
+ elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight);
+ canvas_set_color(canvas, ColorBlack);
+
+ // Ok
+ canvas_draw_icon(canvas, 2, 86, &I_Space_60x18);
+ if(model->ok_pressed) {
+ elements_slightly_rounded_box(canvas, 5, 88, 55, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9);
+ elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space");
+ canvas_set_color(canvas, ColorBlack);
+
+ // Back
+ canvas_draw_icon(canvas, 2, 107, &I_Space_60x18);
+ if(model->back_pressed) {
+ elements_slightly_rounded_box(canvas, 5, 109, 55, 13);
+ canvas_set_color(canvas, ColorWhite);
+ }
+ canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8);
+ elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back");
+}
+
static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {
with_view_model(
hid_keynote->view,
@@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
with_view_model(
hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);
}
+
+void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) {
+ furi_assert(hid_keynote);
+
+ if(vertical) {
+ view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback);
+ view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip);
+
+ } else {
+ view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
+ view_set_orientation(hid_keynote->view, ViewOrientationHorizontal);
+ }
+}
diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h
index 4d4a0a9b1..3e84732aa 100644
--- a/applications/external/hid_app/views/hid_keynote.h
+++ b/applications/external/hid_app/views/hid_keynote.h
@@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote);
View* hid_keynote_get_view(HidKeynote* hid_keynote);
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected);
+
+void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical);
\ No newline at end of file
diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c
deleted file mode 100644
index 7d2303813..000000000
--- a/applications/external/hid_app/views/hid_keynote_vertical.c
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "hid_keynote_vertical.h"
-#include
-#include "../hid.h"
-
-#include "hid_icons.h"
-
-#define TAG "HidKeynoteVertical"
-
-struct HidKeynoteVertical {
- View* view;
- Hid* hid;
-};
-
-typedef struct {
- bool left_pressed;
- bool up_pressed;
- bool right_pressed;
- bool down_pressed;
- bool ok_pressed;
- bool back_pressed;
- bool connected;
- HidTransport transport;
-} HidKeynoteVerticalModel;
-
-static void
- hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
- canvas_draw_triangle(canvas, x, y, 5, 3, dir);
- if(dir == CanvasDirectionBottomToTop) {
- canvas_draw_line(canvas, x, y + 6, x, y - 1);
- } else if(dir == CanvasDirectionTopToBottom) {
- canvas_draw_line(canvas, x, y - 6, x, y + 1);
- } else if(dir == CanvasDirectionRightToLeft) {
- canvas_draw_line(canvas, x + 6, y, x - 1, y);
- } else if(dir == CanvasDirectionLeftToRight) {
- canvas_draw_line(canvas, x - 6, y, x + 1, y);
- }
-}
-
-static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) {
- furi_assert(context);
- HidKeynoteVerticalModel* model = context;
-
- // Header
- if(model->transport == HidTransportBle) {
- if(model->connected) {
- canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
- } else {
- canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
- }
- }
-
- canvas_set_font(canvas, FontPrimary);
- elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
- canvas_set_font(canvas, FontSecondary);
- elements_multiline_text_aligned(
- canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->");
-
- canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);
- canvas_set_font(canvas, FontSecondary);
- elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit");
-
- // Up
- canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);
- if(model->up_pressed) {
- elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
- canvas_set_color(canvas, ColorBlack);
-
- // Down
- canvas_draw_icon(canvas, 21, 45, &I_Button_18x18);
- if(model->down_pressed) {
- elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
- canvas_set_color(canvas, ColorBlack);
-
- // Left
- canvas_draw_icon(canvas, 0, 35, &I_Button_18x18);
- if(model->left_pressed) {
- elements_slightly_rounded_box(canvas, 3, 37, 13, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft);
- canvas_set_color(canvas, ColorBlack);
-
- // Right
- canvas_draw_icon(canvas, 42, 35, &I_Button_18x18);
- if(model->right_pressed) {
- elements_slightly_rounded_box(canvas, 45, 37, 13, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight);
- canvas_set_color(canvas, ColorBlack);
-
- // Ok
- canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
- if(model->ok_pressed) {
- elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
- elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space");
- canvas_set_color(canvas, ColorBlack);
-
- // Back
- canvas_draw_icon(canvas, 63, 45, &I_Space_65x18);
- if(model->back_pressed) {
- elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
- canvas_set_color(canvas, ColorWhite);
- }
- canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
- elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
-}
-
-static void
- hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) {
- with_view_model(
- hid_keynote_vertical->view,
- HidKeynoteVerticalModel * model,
- {
- if(event->type == InputTypePress) {
- if(event->key == InputKeyUp) {
- model->up_pressed = true;
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
- } else if(event->key == InputKeyDown) {
- model->down_pressed = true;
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
- } else if(event->key == InputKeyLeft) {
- model->left_pressed = true;
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
- } else if(event->key == InputKeyRight) {
- model->right_pressed = true;
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
- } else if(event->key == InputKeyOk) {
- model->ok_pressed = true;
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
- } else if(event->key == InputKeyBack) {
- model->back_pressed = true;
- }
- } else if(event->type == InputTypeRelease) {
- if(event->key == InputKeyUp) {
- model->up_pressed = false;
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
- } else if(event->key == InputKeyDown) {
- model->down_pressed = false;
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
- } else if(event->key == InputKeyLeft) {
- model->left_pressed = false;
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
- } else if(event->key == InputKeyRight) {
- model->right_pressed = false;
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
- } else if(event->key == InputKeyOk) {
- model->ok_pressed = false;
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
- } else if(event->key == InputKeyBack) {
- model->back_pressed = false;
- }
- } else if(event->type == InputTypeShort) {
- if(event->key == InputKeyBack) {
- hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
- hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
- hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
- hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
- }
- }
- },
- true);
-}
-
-static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) {
- furi_assert(context);
- HidKeynoteVertical* hid_keynote_vertical = context;
- bool consumed = false;
-
- if(event->type == InputTypeLong && event->key == InputKeyBack) {
- hid_hal_keyboard_release_all(hid_keynote_vertical->hid);
- } else {
- hid_keynote_vertical_process(hid_keynote_vertical, event);
- consumed = true;
- }
-
- return consumed;
-}
-
-HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) {
- HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical));
- hid_keynote_vertical->view = view_alloc();
- hid_keynote_vertical->hid = hid;
- view_set_context(hid_keynote_vertical->view, hid_keynote_vertical);
- view_allocate_model(
- hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel));
- view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback);
- view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback);
-
- with_view_model(
- hid_keynote_vertical->view,
- HidKeynoteVerticalModel * model,
- { model->transport = hid->transport; },
- true);
-
- return hid_keynote_vertical;
-}
-
-void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) {
- furi_assert(hid_keynote_vertical);
- view_free(hid_keynote_vertical->view);
- free(hid_keynote_vertical);
-}
-
-View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) {
- furi_assert(hid_keynote_vertical);
- return hid_keynote_vertical->view;
-}
-
-void hid_keynote_vertical_set_connected_status(
- HidKeynoteVertical* hid_keynote_vertical,
- bool connected) {
- furi_assert(hid_keynote_vertical);
- with_view_model(
- hid_keynote_vertical->view,
- HidKeynoteVerticalModel * model,
- { model->connected = connected; },
- true);
-}
diff --git a/applications/external/hid_app/views/hid_keynote_vertical.h b/applications/external/hid_app/views/hid_keynote_vertical.h
deleted file mode 100644
index bb7134adb..000000000
--- a/applications/external/hid_app/views/hid_keynote_vertical.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include
-
-typedef struct Hid Hid;
-typedef struct HidKeynoteVertical HidKeynoteVertical;
-
-HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid);
-
-void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical);
-
-View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical);
-
-void hid_keynote_vertical_set_connected_status(
- HidKeynoteVertical* hid_keynote_vertical,
- bool connected);
From 21498597194282f0e96b2226359244cc91e2109e Mon Sep 17 00:00:00 2001
From: gid9798 <30450294+gid9798@users.noreply.github.com>
Date: Fri, 23 Jun 2023 00:06:20 +0300
Subject: [PATCH 05/19] Hid app: vertival numpad
---
.../external/hid_app/views/hid_keynote.c | 4 +-
.../external/hid_app/views/hid_numpad.c | 56 ++++++++++++-------
2 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c
index 543363bf6..7d0e125d7 100644
--- a/applications/external/hid_app/views/hid_keynote.c
+++ b/applications/external/hid_app/views/hid_keynote.c
@@ -116,16 +116,16 @@ static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {
HidKeynoteModel* model = context;
// Header
+ canvas_set_font(canvas, FontPrimary);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
- canvas_set_font(canvas, FontPrimary);
+
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
} else {
- canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
}
diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c
index d3b488801..cedb6d341 100644
--- a/applications/external/hid_app/views/hid_numpad.c
+++ b/applications/external/hid_app/views/hid_numpad.c
@@ -39,26 +39,26 @@ typedef struct {
int8_t y;
} HidNumpadPoint;
-#define MARGIN_TOP 0
-#define MARGIN_LEFT 24
+#define MARGIN_TOP 32
+#define MARGIN_LEFT 1
#define KEY_WIDTH 20
#define KEY_HEIGHT 15
#define KEY_PADDING 1
-#define ROW_COUNT 5
-#define COLUMN_COUNT 4
+#define ROW_COUNT 6
+#define COLUMN_COUNT 3
const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
{
{.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
{.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
{.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
- {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
+ // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
{.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
{.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
- {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
+ // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
@@ -69,13 +69,18 @@ const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
{.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
{.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
{.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
- {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
+ // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
},
{
{.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
},
+ {
+ {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
+ {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
+ {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
+ },
};
static void hid_numpad_draw_key(
@@ -128,26 +133,36 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidNumpadModel* model = context;
- if((!model->connected) && (model->transport == HidTransportBle)) {
- canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
- canvas_set_font(canvas, FontPrimary);
- elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad");
-
- canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8);
- canvas_set_font(canvas, FontSecondary);
- elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit");
+ // Header
+ canvas_set_font(canvas, FontPrimary);
+ if(model->transport == HidTransportBle) {
+ if(model->connected) {
+ canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
+ } else {
+ canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
+ }
+ elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
elements_multiline_text_aligned(
- canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection...");
+ canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
+ } else {
+ elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
+ }
+
+ canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
+ canvas_set_font(canvas, FontSecondary);
+ elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
+
+ if(!model->connected && (model->transport == HidTransportBle)) {
return;
}
canvas_set_font(canvas, FontKeyboard);
- uint8_t initY = model->y == 0 ? 0 : 1;
+ uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
- if(model->y > 5) {
- initY = model->y - 4;
- }
+ // if(model->y > ROW_COUNT) {
+ // initY = model->y - (ROW_COUNT - 1);
+ // }
for(uint8_t y = initY; y < ROW_COUNT; y++) {
const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
@@ -269,6 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
hid_numpad->hid = bt_hid;
view_set_context(hid_numpad->view, hid_numpad);
view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
+ view_set_orientation(hid_numpad->view, ViewOrientationVerticalFlip);
view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
From 3ee2223cbd05bf8b77c3528a0eebec5ec161438a Mon Sep 17 00:00:00 2001
From: gid9798 <30450294+gid9798@users.noreply.github.com>
Date: Fri, 23 Jun 2023 21:54:19 +0300
Subject: [PATCH 06/19] Hid app: Flip Numpad
---
applications/external/hid_app/views/hid_numpad.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c
index cedb6d341..c27576862 100644
--- a/applications/external/hid_app/views/hid_numpad.c
+++ b/applications/external/hid_app/views/hid_numpad.c
@@ -284,7 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
hid_numpad->hid = bt_hid;
view_set_context(hid_numpad->view, hid_numpad);
view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
- view_set_orientation(hid_numpad->view, ViewOrientationVerticalFlip);
+ view_set_orientation(hid_numpad->view, ViewOrientationVertical);
view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
From 8ae952f499587dbfb0368881bd9a43662028a207 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 17:50:27 +0300
Subject: [PATCH 07/19] Fix furi_hal_bus issues in AVR Programmer and Signal
Generator
---
.../helpers/avr_isp_worker_rw.c | 19 ++++++--
.../scenes/signal_gen_scene_pwm.c | 47 +++++++++++++++++--
.../signal_generator/signal_gen_app_i.h | 1 +
3 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
index 209551a47..c4323a56d 100644
--- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
+++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
@@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
AvrIspWorkerRW* instance = context;
/* start PWM on &gpio_ext_pa4 */
- furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
+ if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
+ }
FURI_LOG_D(TAG, "Start");
@@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
}
FURI_LOG_D(TAG, "Stop");
- furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
+ if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
+ }
return 0;
}
@@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
instance->chip_arr_ind = avr_isp_chip_arr_size + 1;
/* start PWM on &gpio_ext_pa4 */
- furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
+ bool was_pwm_enabled = false;
+ if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
+ } else {
+ was_pwm_enabled = true;
+ }
do {
if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) {
@@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
} while(0);
- furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
+ if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2) && !was_pwm_enabled) {
+ furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
+ }
if(instance->callback) {
if(instance->chip_arr_ind > avr_isp_chip_arr_size) {
diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
index 7ac3fadda..9e09a472d 100644
--- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
+++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
@@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) {
signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app);
signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY);
- furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
+
+ if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
+ furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
+ } else {
+ furi_hal_pwm_stop(pwm_ch_id[0]);
+ furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
+ }
}
bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
@@ -46,8 +52,32 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty);
} else if(event.event == SignalGenPwmEventChannelChange) {
consumed = true;
- furi_hal_pwm_stop(app->pwm_ch_prev);
- furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ // Stop previous channel PWM
+ if(app->pwm_ch_prev == FuriHalPwmOutputIdTim1PA7) {
+ if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
+ furi_hal_pwm_stop(app->pwm_ch_prev);
+ }
+ } else if(app->pwm_ch_prev == FuriHalPwmOutputIdLptim2PA4) {
+ if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_stop(app->pwm_ch_prev);
+ }
+ }
+ // Start PWM and restart if it was starter already
+ if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) {
+ if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
+ furi_hal_pwm_stop(app->pwm_ch);
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ } else {
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ }
+ } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) {
+ if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_stop(app->pwm_ch);
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ } else {
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ }
+ }
}
}
return consumed;
@@ -56,5 +86,14 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
void signal_gen_scene_pwm_on_exit(void* context) {
SignalGenApp* app = context;
variable_item_list_reset(app->var_item_list);
- furi_hal_pwm_stop(app->pwm_ch);
+
+ if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) {
+ if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
+ furi_hal_pwm_stop(app->pwm_ch);
+ }
+ } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) {
+ if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ furi_hal_pwm_stop(app->pwm_ch);
+ }
+ }
}
diff --git a/applications/external/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h
index 60e4d7ed9..2d31dda60 100644
--- a/applications/external/signal_generator/signal_gen_app_i.h
+++ b/applications/external/signal_generator/signal_gen_app_i.h
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include
From 0540c2743f7fd9670c0ca39eb95cd5ceb8ba3476 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 18:34:33 +0300
Subject: [PATCH 08/19] Update totp
---
applications/external/totp/workers/type_code_common.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c
index bc42fadaa..82a5a028e 100644
--- a/applications/external/totp/workers/type_code_common.c
+++ b/applications/external/totp/workers/type_code_common.c
@@ -52,10 +52,10 @@ void totp_type_code_worker_execute_automation(
while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) {
uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char);
if(char_index > 9) {
- char_index = cb_char - 0x41 + 10;
+ char_index = cb_char - 'A' + 10;
}
- if(char_index > 35) break;
+ if(char_index >= sizeof(hid_number_keys)) break;
uint16_t hid_kb_key = hid_number_keys[char_index];
if(char_index > 9) {
From ae47b9888f0c615175d33cd2921fd0c47c828d60 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 20:23:46 +0300
Subject: [PATCH 09/19] Rework pwm is running check
---
.../helpers/avr_isp_worker_rw.c | 8 ++--
.../scenes/signal_gen_scene_pwm.c | 42 +++++--------------
.../signal_generator/signal_gen_app_i.h | 1 -
firmware/targets/f7/api_symbols.csv | 3 +-
firmware/targets/f7/furi_hal/furi_hal_pwm.c | 9 ++++
firmware/targets/f7/furi_hal/furi_hal_pwm.h | 8 ++++
6 files changed, 34 insertions(+), 37 deletions(-)
diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
index c4323a56d..121f08565 100644
--- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
+++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
@@ -60,7 +60,7 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
AvrIspWorkerRW* instance = context;
/* start PWM on &gpio_ext_pa4 */
- if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
}
@@ -124,7 +124,7 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
}
FURI_LOG_D(TAG, "Stop");
- if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
}
@@ -141,7 +141,7 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
/* start PWM on &gpio_ext_pa4 */
bool was_pwm_enabled = false;
- if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
+ if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
} else {
was_pwm_enabled = true;
@@ -209,7 +209,7 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
} while(0);
- if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2) && !was_pwm_enabled) {
+ if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) {
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
}
diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
index 9e09a472d..1cadb3a1a 100644
--- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
+++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
@@ -34,7 +34,7 @@ void signal_gen_scene_pwm_on_enter(void* context) {
signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY);
- if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
+ if(!furi_hal_pwm_is_running(pwm_ch_id[0])) {
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
} else {
furi_hal_pwm_stop(pwm_ch_id[0]);
@@ -53,30 +53,16 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SignalGenPwmEventChannelChange) {
consumed = true;
// Stop previous channel PWM
- if(app->pwm_ch_prev == FuriHalPwmOutputIdTim1PA7) {
- if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
- furi_hal_pwm_stop(app->pwm_ch_prev);
- }
- } else if(app->pwm_ch_prev == FuriHalPwmOutputIdLptim2PA4) {
- if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
- furi_hal_pwm_stop(app->pwm_ch_prev);
- }
+ if(furi_hal_pwm_is_running(app->pwm_ch_prev)) {
+ furi_hal_pwm_stop(app->pwm_ch_prev);
}
+
// Start PWM and restart if it was starter already
- if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) {
- if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
- furi_hal_pwm_stop(app->pwm_ch);
- furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
- } else {
- furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
- }
- } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) {
- if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
- furi_hal_pwm_stop(app->pwm_ch);
- furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
- } else {
- furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
- }
+ if(furi_hal_pwm_is_running(app->pwm_ch)) {
+ furi_hal_pwm_stop(app->pwm_ch);
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
+ } else {
+ furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
}
}
}
@@ -87,13 +73,7 @@ void signal_gen_scene_pwm_on_exit(void* context) {
SignalGenApp* app = context;
variable_item_list_reset(app->var_item_list);
- if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) {
- if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) {
- furi_hal_pwm_stop(app->pwm_ch);
- }
- } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) {
- if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) {
- furi_hal_pwm_stop(app->pwm_ch);
- }
+ if(furi_hal_pwm_is_running(app->pwm_ch)) {
+ furi_hal_pwm_stop(app->pwm_ch);
}
}
diff --git a/applications/external/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h
index 2d31dda60..60e4d7ed9 100644
--- a/applications/external/signal_generator/signal_gen_app_i.h
+++ b/applications/external/signal_generator/signal_gen_app_i.h
@@ -4,7 +4,6 @@
#include
#include
-#include
#include
#include
diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv
index 26e4c0b3e..41d38cb90 100644
--- a/firmware/targets/f7/api_symbols.csv
+++ b/firmware/targets/f7/api_symbols.csv
@@ -1,5 +1,5 @@
entry,status,name,type,params
-Version,+,30.1,,
+Version,+,30.2,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@@ -1306,6 +1306,7 @@ Function,+,furi_hal_power_sleep,void,
Function,+,furi_hal_power_sleep_available,_Bool,
Function,+,furi_hal_power_suppress_charge_enter,void,
Function,+,furi_hal_power_suppress_charge_exit,void,
+Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId
Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId
diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c
index 7e985cbb1..879460e6b 100644
--- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c
+++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c
@@ -82,6 +82,15 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel) {
}
}
+bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) {
+ if(channel == FuriHalPwmOutputIdTim1PA7) {
+ return furi_hal_bus_is_enabled(FuriHalBusTIM1);
+ } else if(channel == FuriHalPwmOutputIdLptim2PA4) {
+ return furi_hal_bus_is_enabled(FuriHalBusLPTIM2);
+ }
+ return false;
+}
+
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) {
furi_assert(freq > 0);
uint32_t freq_div = 64000000LU / freq;
diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.h b/firmware/targets/f7/furi_hal/furi_hal_pwm.h
index a8682c5fb..16acca05e 100644
--- a/firmware/targets/f7/furi_hal/furi_hal_pwm.h
+++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.h
@@ -9,6 +9,7 @@ extern "C" {
#endif
#include
+#include
typedef enum {
FuriHalPwmOutputIdTim1PA7,
@@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel);
*/
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty);
+/** Is PWM channel running?
+ *
+ * @param[in] channel PWM channel (FuriHalPwmOutputId)
+ * @return bool - true if running
+*/
+bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel);
+
#ifdef __cplusplus
}
#endif
From 8e126112f0c2e343a79b9e9427bc2fb0633d7399 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 21:11:27 +0300
Subject: [PATCH 10/19] OFW PR 2782: NFC: Fix key invalidation logic
by AloneLiberty
---
applications/main/nfc/nfc_cli.c | 4 ++++
lib/nfc/nfc_worker.c | 12 ++++++------
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c
index 6e6e04ca9..0b7e75475 100644
--- a/applications/main/nfc/nfc_cli.c
+++ b/applications/main/nfc/nfc_cli.c
@@ -144,6 +144,10 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) {
break;
}
resp_size = (tx_rx.rx_bits / 8) * 2;
+ if(!resp_size) {
+ printf("No response\r\n");
+ break;
+ }
resp_buffer = malloc(resp_size);
uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size);
resp_buffer[resp_size] = 0;
diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c
index 7425573fb..d6618b5b7 100644
--- a/lib/nfc/nfc_worker.c
+++ b/lib/nfc/nfc_worker.c
@@ -1025,14 +1025,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key A is marked as found and matches the searching key, invalidate it
- uint8_t found_key[6];
- memcpy(found_key, data->block[i].value, 6);
+ MfClassicSectorTrailer* sec_trailer =
+ mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
if(mf_classic_is_key_found(data, i, MfClassicKeyA) &&
- memcmp(found_key, current_key, 6) == 0) {
+ memcmp(sec_trailer->key_a, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
is_key_a_found = false;
FURI_LOG_D(TAG, "Key %dA not found in attack", i);
@@ -1051,14 +1051,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key B is marked as found and matches the searching key, invalidate it
- uint8_t found_key[6];
- memcpy(found_key, data->block[i].value + 10, 6);
+ MfClassicSectorTrailer* sec_trailer =
+ mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
if(mf_classic_is_key_found(data, i, MfClassicKeyB) &&
- memcmp(found_key, current_key, 6) == 0) {
+ memcmp(sec_trailer->key_b, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
is_key_b_found = false;
FURI_LOG_D(TAG, "Key %dB not found in attack", i);
From 6f3f2fa1e7fa18d1f8682e1461b05657ebfb93e7 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 21:14:00 +0300
Subject: [PATCH 11/19] OFW PR 2783: SLIX2 emulation support
by g3gg0
---
.../main/nfc/scenes/nfc_scene_nfc_data_info.c | 161 ++++---
.../nfc/scenes/nfc_scene_nfcv_read_success.c | 6 +-
lib/digital_signal/digital_signal.c | 199 ++++----
lib/nfc/nfc_device.c | 360 +++++++-------
lib/nfc/protocols/nfcv.c | 56 ++-
lib/nfc/protocols/nfcv.h | 49 +-
lib/nfc/protocols/slix.c | 451 ++++++++++++++++--
lib/nfc/protocols/slix.h | 46 +-
8 files changed, 910 insertions(+), 418 deletions(-)
diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
index eb2f939c6..66a9174df 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
@@ -7,6 +7,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ
}
}
+void nfc_scene_slix_build_string(
+ FuriString* temp_str,
+ NfcVData* nfcv_data,
+ SlixTypeFeatures features,
+ const char* type) {
+ furi_string_cat_printf(temp_str, "Type: %s\n", type);
+ furi_string_cat_printf(temp_str, "Keys:\n");
+ if(features & SlixFeatureRead) {
+ furi_string_cat_printf(
+ temp_str,
+ " Read %08llX%s\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4),
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)");
+ }
+ if(features & SlixFeatureWrite) {
+ furi_string_cat_printf(
+ temp_str,
+ " Write %08llX%s\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4),
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)");
+ }
+ if(features & SlixFeaturePrivacy) {
+ furi_string_cat_printf(
+ temp_str,
+ " Privacy %08llX%s\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4),
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)");
+ furi_string_cat_printf(
+ temp_str,
+ " Privacy mode %s\n",
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED");
+ }
+ if(features & SlixFeatureDestroy) {
+ furi_string_cat_printf(
+ temp_str,
+ " Destroy %08llX%s\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4),
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)");
+ }
+ if(features & SlixFeatureEas) {
+ furi_string_cat_printf(
+ temp_str,
+ " EAS %08llX%s\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4),
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)");
+ }
+ if(features & SlixFeatureSignature) {
+ furi_string_cat_printf(
+ temp_str,
+ "Signature %08llX...\n",
+ nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4));
+ }
+ furi_string_cat_printf(
+ temp_str,
+ "DSFID: %02X %s\n",
+ nfcv_data->dsfid,
+ (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
+ furi_string_cat_printf(
+ temp_str,
+ "AFI: %02X %s\n",
+ nfcv_data->afi,
+ (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
+ furi_string_cat_printf(
+ temp_str,
+ "EAS: %s\n",
+ (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked");
+
+ if(features & SlixFeatureProtection) {
+ furi_string_cat_printf(
+ temp_str,
+ "PPL: %s\n",
+ (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked");
+ furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer);
+ furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition);
+ }
+}
+
void nfc_scene_nfc_data_info_on_enter(void* context) {
Nfc* nfc = context;
Widget* widget = nfc->widget;
@@ -76,95 +153,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
}
furi_string_cat_printf(temp_str, "\n");
- furi_string_cat_printf(
- temp_str,
- "DSFID: %02X %s\n",
- nfcv_data->dsfid,
- (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
- furi_string_cat_printf(
- temp_str,
- "AFI: %02X %s\n",
- nfcv_data->afi,
- (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
- furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref);
- furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
- furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
+ furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref);
+ furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num);
+ furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size);
switch(dev_data->nfcv_data.sub_type) {
case NfcVTypePlain:
furi_string_cat_printf(temp_str, "Type: Plain\n");
break;
case NfcVTypeSlix:
- furi_string_cat_printf(temp_str, "Type: SLIX\n");
- furi_string_cat_printf(temp_str, "Keys:\n");
- furi_string_cat_printf(
- temp_str,
- " EAS %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
+ nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX");
break;
case NfcVTypeSlixS:
- furi_string_cat_printf(temp_str, "Type: SLIX-S\n");
- furi_string_cat_printf(temp_str, "Keys:\n");
- furi_string_cat_printf(
- temp_str,
- " Read %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
- furi_string_cat_printf(
- temp_str,
- " Write %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
- furi_string_cat_printf(
- temp_str,
- " Privacy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
- furi_string_cat_printf(
- temp_str,
- " Destroy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
- furi_string_cat_printf(
- temp_str,
- " EAS %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
+ nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S");
break;
case NfcVTypeSlixL:
- furi_string_cat_printf(temp_str, "Type: SLIX-L\n");
- furi_string_cat_printf(temp_str, "Keys:\n");
- furi_string_cat_printf(
- temp_str,
- " Privacy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
- furi_string_cat_printf(
- temp_str,
- " Destroy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
- furi_string_cat_printf(
- temp_str,
- " EAS %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
+ nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L");
break;
case NfcVTypeSlix2:
- furi_string_cat_printf(temp_str, "Type: SLIX2\n");
- furi_string_cat_printf(temp_str, "Keys:\n");
- furi_string_cat_printf(
- temp_str,
- " Read %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
- furi_string_cat_printf(
- temp_str,
- " Write %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
- furi_string_cat_printf(
- temp_str,
- " Privacy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
- furi_string_cat_printf(
- temp_str,
- " Destroy %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
- furi_string_cat_printf(
- temp_str,
- " EAS %08llX\n",
- nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
+ nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2");
break;
default:
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
index bdf7692cc..04e60611d 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
@@ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) {
Nfc* nfc = context;
NfcDeviceData* dev_data = &nfc->dev->dev_data;
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
- NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
// Setup view
Widget* widget = nfc->widget;
widget_add_button_element(
@@ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) {
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
break;
}
- furi_string_cat_printf(temp_str, "UID:");
+ furi_string_cat_printf(temp_str, "UID:\n");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
furi_string_cat_printf(temp_str, "\n");
- furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
- furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
+ furi_string_cat_printf(temp_str, "(see More->Info for details)\n");
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c
index 749c12c7d..25adb878b 100644
--- a/lib/digital_signal/digital_signal.c
+++ b/lib/digital_signal/digital_signal.c
@@ -51,8 +51,16 @@ struct DigitalSignalInternals {
#define T_TIM 1562 /* 15.625 ns *100 */
#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */
+/* end marker in DMA ringbuffer, will get written into timer register at the end */
+#define SEQ_TIMER_MAX 0xFFFFFFFF
+
+/* time to wait in loops before returning */
+#define SEQ_LOCK_WAIT_MS 10UL
+#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64)
+
/* maximum entry count of the sequence dma ring buffer */
-#define SEQUENCE_DMA_RINGBUFFER_SIZE 32
+#define RINGBUFFER_SIZE 128
+
/* maximum number of DigitalSignals in a sequence */
#define SEQUENCE_SIGNALS_SIZE 32
/*
@@ -214,12 +222,12 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
for(size_t pos = 0; pos < signal->edge_cnt; pos++) {
uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder;
if(pulse_duration < 10 || pulse_duration > 10000000) {
- /*FURI_LOG_D(
+ FURI_LOG_D(
TAG,
"[prepare] pulse_duration out of range: %lu = %lu * %llu",
pulse_duration,
signal->edge_timings[pos],
- internals->factor);*/
+ internals->factor);
pulse_duration = 100;
}
uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM;
@@ -243,20 +251,16 @@ static void digital_signal_stop_timer() {
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_DisableDMAReq_UPDATE(TIM2);
- if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
- furi_hal_bus_disable(FuriHalBusTIM2);
- }
+ furi_hal_bus_disable(FuriHalBusTIM2);
}
static void digital_signal_setup_timer() {
- if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
- furi_hal_bus_enable(FuriHalBusTIM2);
- }
+ furi_hal_bus_enable(FuriHalBusTIM2);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
- LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
+ LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX);
LL_TIM_SetCounter(TIM2, 0);
}
@@ -339,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
sequence->bake = false;
sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer));
- sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE;
+ sequence->dma_buffer->size = RINGBUFFER_SIZE;
sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t));
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
@@ -458,42 +462,26 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
return ret;
}
-static void digital_sequence_update_pos(DigitalSequence* sequence) {
- struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
-
- dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
-}
-
-static const uint32_t wait_ms = 10;
-static const uint32_t wait_ticks = wait_ms * 1000 * 64;
-
static void digital_sequence_finish(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
- uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
- uint32_t last_pos = dma_buffer->read_pos;
-
- digital_sequence_update_pos(sequence);
-
- /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */
- if(dma_buffer->read_pos == end_pos) {
+ /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */
+ if(TIM2->ARR == SEQ_TIMER_MAX) {
break;
}
-
- if(last_pos != dma_buffer->read_pos) { //-V547
- prev_timer = DWT->CYCCNT;
- }
- if(DWT->CYCCNT - prev_timer > wait_ticks) {
- /*FURI_LOG_D(
+ if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
+ dma_buffer->read_pos =
+ RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
+ FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
- wait_ms,
+ SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
- dma_buffer->write_pos);*/
+ dma_buffer->write_pos);
break;
}
} while(1);
@@ -508,34 +496,42 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
- uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
- uint32_t last_pos = dma_buffer->read_pos;
- digital_sequence_update_pos(sequence);
+ dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
- if(dma_buffer->read_pos != end_pos) {
+ uint32_t free =
+ (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE;
+
+ if(free > 2) {
break;
}
- if(last_pos != dma_buffer->read_pos) { //-V547
- prev_timer = DWT->CYCCNT;
- }
- if(DWT->CYCCNT - prev_timer > wait_ticks) {
- /*FURI_LOG_D(
+ if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
+ FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
- wait_ms,
+ SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
- dma_buffer->write_pos);*/
+ dma_buffer->write_pos);
+ break;
+ }
+ if(TIM2->ARR == SEQ_TIMER_MAX) {
+ FURI_LOG_D(
+ TAG,
+ "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)",
+ TIM2->ARR,
+ dma_buffer->read_pos,
+ dma_buffer->write_pos);
break;
}
} while(1);
}
dma_buffer->buffer[dma_buffer->write_pos] = length;
- dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
- dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF;
+ dma_buffer->write_pos++;
+ dma_buffer->write_pos %= RINGBUFFER_SIZE;
+ dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX;
}
bool digital_sequence_send(DigitalSequence* sequence) {
@@ -557,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) {
return true;
}
- int32_t remainder = 0;
- bool traded_first = false;
+ if(!sequence->sequence_used) {
+ return false;
+ }
- FURI_CRITICAL_ENTER();
+ int32_t remainder = 0;
+ uint32_t trade_for_next = 0;
+ uint32_t seq_pos_next = 1;
dma_buffer->dma_active = false;
- dma_buffer->buffer[0] = 0xFFFFFFFF;
+ dma_buffer->buffer[0] = SEQ_TIMER_MAX;
dma_buffer->read_pos = 0;
dma_buffer->write_pos = 0;
- for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) {
- uint8_t signal_index = sequence->sequence[seq_pos];
- DigitalSignal* sig = sequence->signals[signal_index];
- bool last_signal = ((seq_pos + 1) == sequence->sequence_used);
+ /* already prepare the current signal pointer */
+ DigitalSignal* sig = sequence->signals[sequence->sequence[0]];
+ DigitalSignal* sig_next = NULL;
+ /* re-use the GPIO buffer from the first signal */
+ sequence->gpio_buff = sig->internals->gpio_buff;
- /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */
- if(seq_pos == 0) {
- sequence->gpio_buff = sig->internals->gpio_buff;
+ FURI_CRITICAL_ENTER();
+
+ while(sig) {
+ bool last_signal = (seq_pos_next >= sequence->sequence_used);
+
+ if(!last_signal) {
+ sig_next = sequence->signals[sequence->sequence[seq_pos_next++]];
}
for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) {
- if(traded_first) {
- traded_first = false;
- continue;
- }
- uint32_t pulse_length = 0;
- bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries);
+ bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries);
+ uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next;
- pulse_length = sig->reload_reg_buff[pulse_pos];
+ trade_for_next = 0;
/* when we are too late more than half a tick, make the first edge temporarily longer */
if(remainder >= T_TIM_DIV2) {
remainder -= T_TIM;
pulse_length += 1;
}
- remainder += sig->internals->reload_reg_remainder;
- /* last pulse in that signal and have a next signal? */
- if(last_pulse) {
- if((seq_pos + 1) < sequence->sequence_used) {
- DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]];
+ /* last pulse in current signal and have a next signal? */
+ if(last_pulse && sig_next) {
+ /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
+ beware, we do not want the level after the last edge, but the last level before that edge */
+ bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
- /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */
- /* beware, we do not want the level after the last edge, but the last level before that edge */
- bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
-
- /* take from the next, add it to the current if they have the same level */
- if(end_level == sig_next->start_level) {
- pulse_length += sig_next->reload_reg_buff[0];
- traded_first = true;
- }
+ /* if they have the same level, pass the duration to the next pulse(s) */
+ if(end_level == sig_next->start_level) {
+ trade_for_next = pulse_length;
}
}
- digital_sequence_queue_pulse(sequence, pulse_length);
+ /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */
+ if(!trade_for_next) {
+ digital_sequence_queue_pulse(sequence, pulse_length);
- /* start transmission when buffer was filled enough */
- bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4);
+ if(!dma_buffer->dma_active) {
+ /* start transmission when buffer was filled enough */
+ bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2);
- /* or it was the last pulse */
- if(last_pulse && last_signal) {
- start_send = true;
- }
+ /* or it was the last pulse */
+ if(last_pulse && last_signal) {
+ start_send = true;
+ }
- /* start transmission */
- if(start_send && !dma_buffer->dma_active) {
- digital_sequence_setup_dma(sequence);
- digital_signal_setup_timer();
+ /* start transmission */
+ if(start_send) {
+ digital_sequence_setup_dma(sequence);
+ digital_signal_setup_timer();
- /* if the send time is specified, wait till the core timer passed beyond that time */
- if(sequence->send_time_active) {
- sequence->send_time_active = false;
- while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
+ /* if the send time is specified, wait till the core timer passed beyond that time */
+ if(sequence->send_time_active) {
+ sequence->send_time_active = false;
+ while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
+ }
+ }
+ digital_signal_start_timer();
+ dma_buffer->dma_active = true;
}
}
- digital_signal_start_timer();
- dma_buffer->dma_active = true;
}
}
+
+ remainder += sig->internals->reload_reg_remainder;
+ sig = sig_next;
+ sig_next = NULL;
}
/* wait until last dma transaction was finished */
- digital_sequence_finish(sequence);
FURI_CRITICAL_EXIT();
+ digital_sequence_finish(sequence);
return true;
}
diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c
index 6db695768..cd9eb336c 100644
--- a/lib/nfc/nfc_device.c
+++ b/lib/nfc/nfc_device.c
@@ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
return parsed;
}
-static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) {
+static bool nfc_device_save_slix_data(
+ FlipperFormat* file,
+ NfcDevice* dev,
+ SlixTypeFeatures features,
+ const char* type) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
- if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break;
- if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
+ char msg[64];
+ snprintf(msg, sizeof(msg), "%s specific data", type);
+ if(!flipper_format_write_comment_cstr(file, msg)) break;
+ if(!flipper_format_write_comment_cstr(
+ file, "Passwords are optional. If password is omitted, any password is accepted"))
break;
+
+ if(features & SlixFeatureRead) {
+ if(data->flags & NfcVSlixDataFlagsHasKeyRead) {
+ if(!flipper_format_write_hex(
+ file, "Password Read", data->key_read, sizeof(data->key_read)))
+ break;
+ }
+ }
+ if(features & SlixFeatureWrite) {
+ if(data->flags & NfcVSlixDataFlagsHasKeyWrite) {
+ if(!flipper_format_write_hex(
+ file, "Password Write", data->key_write, sizeof(data->key_write)))
+ break;
+ }
+ }
+ if(features & SlixFeaturePrivacy) {
+ if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) {
+ if(!flipper_format_write_hex(
+ file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
+ break;
+ }
+ }
+ if(features & SlixFeatureDestroy) {
+ if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) {
+ if(!flipper_format_write_hex(
+ file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
+ break;
+ }
+ }
+ if(features & SlixFeatureEas) {
+ if(data->flags & NfcVSlixDataFlagsHasKeyEas) {
+ if(!flipper_format_write_hex(
+ file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
+ break;
+ }
+ }
+ if(features & SlixFeatureSignature) {
+ if(!flipper_format_write_comment_cstr(
+ file,
+ "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key."))
+ break;
+ if(!flipper_format_write_hex(
+ file, "Signature", data->signature, sizeof(data->signature)))
+ break;
+ }
+ if(features & SlixFeaturePrivacy) {
+ bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false;
+ if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break;
+ }
+ if(features & SlixFeatureProtection) {
+ if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break;
+ if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break;
+ if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1))
+ break;
+ }
saved = true;
} while(false);
return saved;
}
-bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) {
+bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
- if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
-
- parsed = true;
- } while(false);
-
- return parsed;
-}
-
-static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
- bool saved = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
-
- do {
- if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break;
- if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Write", data->key_write, sizeof(data->key_write)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
- saved = true;
- } while(false);
-
- return saved;
-}
-
-bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
- bool parsed = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
- memset(data, 0, sizeof(NfcVSlixData));
-
- do {
- if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Write", data->key_write, sizeof(data->key_write)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
-
- parsed = true;
- } while(false);
-
- return parsed;
-}
-
-static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
- bool saved = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
-
- do {
- if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break;
- if(!flipper_format_write_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
- saved = true;
- } while(false);
-
- return saved;
-}
-
-bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
- bool parsed = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
- memset(data, 0, sizeof(NfcVSlixData));
-
- do {
- if(!flipper_format_read_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
-
- parsed = true;
- } while(false);
-
- return parsed;
-}
-
-static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) {
- bool saved = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
-
- do {
- if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break;
- if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Write", data->key_write, sizeof(data->key_write)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_write_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
- saved = true;
- } while(false);
-
- return saved;
-}
-
-bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524
- bool parsed = false;
- NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
- memset(data, 0, sizeof(NfcVSlixData));
-
- do {
- if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Write", data->key_write, sizeof(data->key_write)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
- break;
- if(!flipper_format_read_hex(
- file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
- break;
- if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
- break;
- if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
+ data->flags = 0;
+ if(features & SlixFeatureRead) {
+ if(flipper_format_key_exist(file, "Password Read")) {
+ if(!flipper_format_read_hex(
+ file, "Password Read", data->key_read, sizeof(data->key_read))) {
+ FURI_LOG_D(TAG, "Failed reading Password Read");
+ break;
+ }
+ data->flags |= NfcVSlixDataFlagsHasKeyRead;
+ }
+ }
+ if(features & SlixFeatureWrite) {
+ if(flipper_format_key_exist(file, "Password Write")) {
+ if(!flipper_format_read_hex(
+ file, "Password Write", data->key_write, sizeof(data->key_write))) {
+ FURI_LOG_D(TAG, "Failed reading Password Write");
+ break;
+ }
+ data->flags |= NfcVSlixDataFlagsHasKeyWrite;
+ }
+ }
+ if(features & SlixFeaturePrivacy) {
+ if(flipper_format_key_exist(file, "Password Privacy")) {
+ if(!flipper_format_read_hex(
+ file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) {
+ FURI_LOG_D(TAG, "Failed reading Password Privacy");
+ break;
+ }
+ data->flags |= NfcVSlixDataFlagsHasKeyPrivacy;
+ }
+ }
+ if(features & SlixFeatureDestroy) {
+ if(flipper_format_key_exist(file, "Password Destroy")) {
+ if(!flipper_format_read_hex(
+ file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) {
+ FURI_LOG_D(TAG, "Failed reading Password Destroy");
+ break;
+ }
+ data->flags |= NfcVSlixDataFlagsHasKeyDestroy;
+ }
+ }
+ if(features & SlixFeatureEas) {
+ if(flipper_format_key_exist(file, "Password EAS")) {
+ if(!flipper_format_read_hex(
+ file, "Password EAS", data->key_eas, sizeof(data->key_eas))) {
+ FURI_LOG_D(TAG, "Failed reading Password EAS");
+ break;
+ }
+ data->flags |= NfcVSlixDataFlagsHasKeyEas;
+ }
+ }
+ if(features & SlixFeatureSignature) {
+ if(!flipper_format_read_hex(
+ file, "Signature", data->signature, sizeof(data->signature))) {
+ FURI_LOG_D(TAG, "Failed reading Signature");
+ break;
+ }
+ }
+ if(features & SlixFeaturePrivacy) {
+ bool privacy;
+ if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) {
+ FURI_LOG_D(TAG, "Failed reading Privacy Mode");
+ break;
+ }
+ if(privacy) {
+ data->flags |= NfcVSlixDataFlagsPrivacy;
+ }
+ }
+ if(features & SlixFeatureProtection) {
+ if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) {
+ FURI_LOG_D(TAG, "Failed reading Protection pointer");
+ break;
+ }
+ if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) {
+ FURI_LOG_D(TAG, "Failed reading Protection condition");
+ break;
+ }
+ }
parsed = true;
} while(false);
@@ -859,7 +848,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
file, "Data Content", data->data, data->block_num * data->block_size))
break;
if(!flipper_format_write_comment_cstr(
- file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info"))
+ file,
+ "First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info"))
break;
if(!flipper_format_write_hex(
file, "Security Status", data->security_status, 1 + data->block_num))
@@ -877,16 +867,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
saved = true;
break;
case NfcVTypeSlix:
- saved = nfc_device_save_slix_data(file, dev);
+ saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX");
break;
case NfcVTypeSlixS:
- saved = nfc_device_save_slix_s_data(file, dev);
+ saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S");
break;
case NfcVTypeSlixL:
- saved = nfc_device_save_slix_l_data(file, dev);
+ saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L");
break;
case NfcVTypeSlix2:
- saved = nfc_device_save_slix2_data(file, dev);
+ saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2");
break;
default:
break;
@@ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
uint32_t temp_uint32 = 0;
uint8_t temp_value = 0;
- if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break;
- if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break;
- if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break;
- if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break;
- data->block_num = temp_uint32;
- if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break;
- if(!flipper_format_read_hex(
- file, "Data Content", data->data, data->block_num * data->block_size))
+ if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) {
+ FURI_LOG_D(TAG, "Failed reading DSFID");
break;
+ }
+ if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) {
+ FURI_LOG_D(TAG, "Failed reading AFI");
+ break;
+ }
+ if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) {
+ FURI_LOG_D(TAG, "Failed reading IC Reference");
+ break;
+ }
+ if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) {
+ FURI_LOG_D(TAG, "Failed reading Block Count");
+ break;
+ }
+ data->block_num = temp_uint32;
+ if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) {
+ FURI_LOG_D(TAG, "Failed reading Block Size");
+ break;
+ }
+ if(!flipper_format_read_hex(
+ file, "Data Content", data->data, data->block_num * data->block_size)) {
+ FURI_LOG_D(TAG, "Failed reading Data Content");
+ break;
+ }
/* optional, as added later */
if(flipper_format_key_exist(file, "Security Status")) {
if(!flipper_format_read_hex(
- file, "Security Status", data->security_status, 1 + data->block_num))
+ file, "Security Status", data->security_status, 1 + data->block_num)) {
+ FURI_LOG_D(TAG, "Failed reading Security Status");
break;
+ }
+ }
+ if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) {
+ FURI_LOG_D(TAG, "Failed reading Subtype");
+ break;
}
- if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break;
data->sub_type = temp_value;
switch(data->sub_type) {
@@ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
parsed = true;
break;
case NfcVTypeSlix:
- parsed = nfc_device_load_slix_data(file, dev);
+ parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix);
break;
case NfcVTypeSlixS:
- parsed = nfc_device_load_slix_s_data(file, dev);
+ parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS);
break;
case NfcVTypeSlixL:
- parsed = nfc_device_load_slix_l_data(file, dev);
+ parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL);
break;
case NfcVTypeSlix2:
- parsed = nfc_device_load_slix2_data(file, dev);
+ parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2);
break;
default:
break;
diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c
index 3c37153d8..017b06cae 100644
--- a/lib/nfc/protocols/nfcv.c
+++ b/lib/nfc/protocols/nfcv.c
@@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n
return false;
}
+ /* clear all know sub type data before reading them */
+ memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data));
+
if(slix_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX detected");
nfcv_data->sub_type = NfcVTypeSlix;
} else if(slix2_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX2 detected");
nfcv_data->sub_type = NfcVTypeSlix2;
+ if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) {
+ return false;
+ }
} else if(slix_s_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX-S detected");
nfcv_data->sub_type = NfcVTypeSlixS;
@@ -612,9 +618,34 @@ void nfcv_emu_handle_packet(
if(ctx->flags & NFCV_REQ_FLAG_AFI) {
uint8_t afi = nfcv_data->frame[ctx->payload_offset];
- if(afi == nfcv_data->afi) {
- respond = true;
+
+ uint8_t family = (afi & 0xF0);
+ uint8_t subfamily = (afi & 0x0F);
+
+ if(family) {
+ if(subfamily) {
+ /* selected family and subfamily only */
+ if(afi == nfcv_data->afi) {
+ respond = true;
+ }
+ } else {
+ /* selected family, any subfamily */
+ if(family == (nfcv_data->afi & 0xf0)) {
+ respond = true;
+ }
+ }
+ } else {
+ if(subfamily) {
+ /* proprietary subfamily only */
+ if(afi == nfcv_data->afi) {
+ respond = true;
+ }
+ } else {
+ /* all families and subfamilies */
+ respond = true;
+ }
}
+
} else {
respond = true;
}
@@ -740,13 +771,19 @@ void nfcv_emu_handle_packet(
case NFCV_CMD_READ_MULTI_BLOCK:
case NFCV_CMD_READ_BLOCK: {
uint8_t block = nfcv_data->frame[ctx->payload_offset];
- uint8_t blocks = 1;
+ int blocks = 1;
if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) {
blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1;
}
- if(block + blocks <= nfcv_data->block_num) {
+ /* limit the maximum block count, underflow accepted */
+ if(block + blocks > nfcv_data->block_num) {
+ blocks = nfcv_data->block_num - block;
+ }
+
+ /* only respond with the valid blocks, if there are any */
+ if(blocks > 0) {
uint8_t buffer_pos = 0;
ctx->response_buffer[buffer_pos++] = NFCV_NOERROR;
@@ -773,10 +810,13 @@ void nfcv_emu_handle_packet(
ctx->response_flags,
ctx->send_time);
} else {
- ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
- ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
- nfcv_emu_send(
- tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
+ /* reply with an error only in addressed or selected mode */
+ if(ctx->addressed || ctx->selected) {
+ ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
+ ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
+ nfcv_emu_send(
+ tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
+ }
}
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h
index 87a696737..e4139de99 100644
--- a/lib/nfc/protocols/nfcv.h
+++ b/lib/nfc/protocols/nfcv.h
@@ -139,8 +139,10 @@ typedef enum {
} NfcVErrorcodes;
typedef enum {
- NfcVLockBitDsfid = 1,
- NfcVLockBitAfi = 2,
+ NfcVLockBitDsfid = 1 << 0,
+ NfcVLockBitAfi = 1 << 1,
+ NfcVLockBitEas = 1 << 2,
+ NfcVLockBitPpl = 1 << 3,
} NfcVLockBits;
typedef enum {
@@ -168,14 +170,55 @@ typedef enum {
NfcVSendFlagsHighRate = 1 << 4
} NfcVSendFlags;
+/* SLIX specific config flags */
+typedef enum {
+ NfcVSlixDataFlagsNone = 0,
+ NfcVSlixDataFlagsHasKeyRead = 1 << 0,
+ NfcVSlixDataFlagsHasKeyWrite = 1 << 1,
+ NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2,
+ NfcVSlixDataFlagsHasKeyDestroy = 1 << 3,
+ NfcVSlixDataFlagsHasKeyEas = 1 << 4,
+ NfcVSlixDataFlagsValidKeyRead = 1 << 8,
+ NfcVSlixDataFlagsValidKeyWrite = 1 << 9,
+ NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10,
+ NfcVSlixDataFlagsValidKeyDestroy = 1 << 11,
+ NfcVSlixDataFlagsValidKeyEas = 1 << 12,
+ NfcVSlixDataFlagsPrivacy = 1 << 16,
+ NfcVSlixDataFlagsDestroyed = 1 << 17
+} NfcVSlixDataFlags;
+
+/* abstract the file read/write operations for all SLIX types to reduce duplicated code */
+typedef enum {
+ SlixFeatureRead = 1 << 0,
+ SlixFeatureWrite = 1 << 1,
+ SlixFeaturePrivacy = 1 << 2,
+ SlixFeatureDestroy = 1 << 3,
+ SlixFeatureEas = 1 << 4,
+ SlixFeatureSignature = 1 << 5,
+ SlixFeatureProtection = 1 << 6,
+
+ SlixFeatureSlix = SlixFeatureEas,
+ SlixFeatureSlixS =
+ (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
+ SlixFeatureEas),
+ SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas),
+ SlixFeatureSlix2 =
+ (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
+ SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection),
+} SlixTypeFeatures;
+
typedef struct {
+ uint32_t flags;
uint8_t key_read[4];
uint8_t key_write[4];
uint8_t key_privacy[4];
uint8_t key_destroy[4];
uint8_t key_eas[4];
uint8_t rand[2];
- bool privacy;
+ uint8_t signature[32];
+ /* SLIX2 options */
+ uint8_t pp_pointer;
+ uint8_t pp_condition;
} NfcVSlixData;
typedef union {
diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c
index 1c14c0bf9..9f8ffa143 100644
--- a/lib/nfc/protocols/slix.c
+++ b/lib/nfc/protocols/slix.c
@@ -9,6 +9,120 @@
#define TAG "SLIX"
+ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
+ furi_assert(nfc_data);
+ furi_assert(nfcv_data);
+
+ uint8_t rxBuf[32];
+ uint16_t received = 0;
+ ReturnCode ret = ERR_NONE;
+
+ FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION...");
+
+ for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
+ uint8_t cmd[] = {};
+ uint8_t uid[NFCV_UID_LENGTH];
+
+ /* UID is stored reversed in requests */
+ for(int pos = 0; pos < nfc_data->uid_len; pos++) {
+ uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
+ }
+
+ ReturnCode ret = rfalNfcvPollerTransceiveReq(
+ NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION,
+ RFAL_NFCV_REQ_FLAG_DEFAULT,
+ NFCV_MANUFACTURER_NXP,
+ uid,
+ cmd,
+ sizeof(cmd),
+ rxBuf,
+ sizeof(rxBuf),
+ &received);
+
+ if(ret == ERR_NONE) {
+ break;
+ }
+ }
+
+ if(ret != ERR_NONE || received != 8) {
+ FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
+ return ret;
+ }
+ FURI_LOG_D(TAG, "Success...");
+
+ NfcVSlixData* slix = &nfcv_data->sub_data.slix;
+ slix->pp_pointer = rxBuf[1];
+ slix->pp_condition = rxBuf[2];
+
+ /* convert NXP's to our internal lock bits format */
+ nfcv_data->security_status[0] = 0;
+ nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0;
+ nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0;
+ nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0;
+ nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0;
+
+ return ERR_NONE;
+}
+
+ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
+ furi_assert(nfc_data);
+ furi_assert(nfcv_data);
+
+ uint8_t rxBuf[64];
+ uint16_t received = 0;
+ ReturnCode ret = ERR_NONE;
+
+ FURI_LOG_D(TAG, "Read SIGNATURE...");
+
+ for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
+ uint8_t cmd[] = {};
+ uint8_t uid[NFCV_UID_LENGTH];
+
+ /* UID is stored reversed in requests */
+ for(int pos = 0; pos < nfc_data->uid_len; pos++) {
+ uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
+ }
+
+ ReturnCode ret = rfalNfcvPollerTransceiveReq(
+ NFCV_CMD_NXP_READ_SIGNATURE,
+ RFAL_NFCV_REQ_FLAG_DEFAULT,
+ NFCV_MANUFACTURER_NXP,
+ uid,
+ cmd,
+ sizeof(cmd),
+ rxBuf,
+ sizeof(rxBuf),
+ &received);
+
+ if(ret == ERR_NONE) {
+ break;
+ }
+ }
+
+ if(ret != ERR_NONE || received != 33) {
+ FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
+ return ret;
+ }
+ FURI_LOG_D(TAG, "Success...");
+
+ NfcVSlixData* slix = &nfcv_data->sub_data.slix;
+ memcpy(slix->signature, &rxBuf[1], 32);
+
+ return ERR_NONE;
+}
+
+ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
+ ReturnCode ret = ERR_NONE;
+
+ ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data);
+ if(ret != ERR_NONE) {
+ return ret;
+ }
+ ret = slix2_read_signature(nfc_data, nfcv_data);
+
+ return ret;
+}
+
static uint32_t slix_read_be(uint8_t* data, uint32_t length) {
uint32_t value = 0;
@@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
return ret;
}
+static void slix_generic_pass_infos(
+ uint8_t password_id,
+ NfcVSlixData* slix,
+ uint8_t** password,
+ uint32_t* flag_valid,
+ uint32_t* flag_set) {
+ switch(password_id) {
+ case SLIX_PASS_READ:
+ *password = slix->key_read;
+ *flag_valid = NfcVSlixDataFlagsValidKeyRead;
+ *flag_set = NfcVSlixDataFlagsHasKeyRead;
+ break;
+ case SLIX_PASS_WRITE:
+ *password = slix->key_write;
+ *flag_valid = NfcVSlixDataFlagsValidKeyWrite;
+ *flag_set = NfcVSlixDataFlagsHasKeyWrite;
+ break;
+ case SLIX_PASS_PRIVACY:
+ *password = slix->key_privacy;
+ *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy;
+ *flag_set = NfcVSlixDataFlagsHasKeyPrivacy;
+ break;
+ case SLIX_PASS_DESTROY:
+ *password = slix->key_destroy;
+ *flag_valid = NfcVSlixDataFlagsValidKeyDestroy;
+ *flag_set = NfcVSlixDataFlagsHasKeyDestroy;
+ break;
+ case SLIX_PASS_EASAFI:
+ *password = slix->key_eas;
+ *flag_valid = NfcVSlixDataFlagsValidKeyEas;
+ *flag_set = NfcVSlixDataFlagsHasKeyEas;
+ break;
+ default:
+ break;
+ }
+}
+
bool slix_generic_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
@@ -150,7 +301,8 @@ bool slix_generic_protocol_filter(
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
- if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
+ if((slix->flags & NfcVSlixDataFlagsPrivacy) &&
+ ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
snprintf(
nfcv_data->last_command,
@@ -186,66 +338,73 @@ bool slix_generic_protocol_filter(
}
case NFCV_CMD_NXP_SET_PASSWORD: {
+ /* the password to be set is the first parameter */
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
+ /* right after that is the XORed password */
+ uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
+ /* only handle if the password type is supported */
if(!(password_id & password_supported)) {
break;
}
- uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
+ /* fetch the last RAND value */
uint8_t* rand = slix->rand;
- uint8_t* password = NULL;
+
+ /* first calc the password that has been sent */
uint8_t password_rcv[4];
-
- switch(password_id) {
- case SLIX_PASS_READ:
- password = slix->key_read;
- break;
- case SLIX_PASS_WRITE:
- password = slix->key_write;
- break;
- case SLIX_PASS_PRIVACY:
- password = slix->key_privacy;
- break;
- case SLIX_PASS_DESTROY:
- password = slix->key_destroy;
- break;
- case SLIX_PASS_EASAFI:
- password = slix->key_eas;
- break;
- default:
- break;
+ for(int pos = 0; pos < 4; pos++) {
+ password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
}
+ uint32_t pass_received = slix_read_be(password_rcv, 4);
+ /* then determine the password type (or even update if not set yet) */
+ uint8_t* password = NULL;
+ uint32_t flag_valid = 0;
+ uint32_t flag_set = 0;
+
+ slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
+
+ /* when the password is not supported, return silently */
if(!password) {
break;
}
- for(int pos = 0; pos < 4; pos++) {
- password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
- }
- uint32_t pass_expect = slix_read_be(password, 4);
- uint32_t pass_received = slix_read_be(password_rcv, 4);
+ /* check if the password is known */
+ bool pass_valid = false;
+ uint32_t pass_expect = 0;
- /* if the password is all-zeroes, just accept any password*/
- if(!pass_expect || pass_expect == pass_received) {
+ if(slix->flags & flag_set) {
+ /* if so, fetch the stored password and compare */
+ pass_expect = slix_read_be(password, 4);
+ pass_valid = (pass_expect == pass_received);
+ } else {
+ /* if not known, just accept it and store that password */
+ memcpy(password, password_rcv, 4);
+ nfcv_data->modified = true;
+ slix->flags |= flag_set;
+
+ pass_valid = true;
+ }
+
+ /* if the pass was valid or accepted for other reasons, continue */
+ if(pass_valid) {
+ slix->flags |= flag_valid;
+
+ /* handle actions when a correct password was given, aside of setting the flag */
switch(password_id) {
- case SLIX_PASS_READ:
- break;
- case SLIX_PASS_WRITE:
- break;
case SLIX_PASS_PRIVACY:
- slix->privacy = false;
+ slix->flags &= ~NfcVSlixDataFlagsPrivacy;
nfcv_data->modified = true;
break;
case SLIX_PASS_DESTROY:
+ slix->flags |= NfcVSlixDataFlagsDestroyed;
FURI_LOG_D(TAG, "Pooof! Got destroyed");
break;
- case SLIX_PASS_EASAFI:
- break;
default:
break;
}
+
ctx->response_buffer[0] = NFCV_NOERROR;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
@@ -268,6 +427,49 @@ bool slix_generic_protocol_filter(
break;
}
+ case NFCV_CMD_NXP_WRITE_PASSWORD: {
+ uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
+
+ if(!(password_id & password_supported)) {
+ break;
+ }
+
+ uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1];
+ uint8_t* password = NULL;
+ uint32_t flag_valid = 0;
+ uint32_t flag_set = 0;
+
+ slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
+
+ /* when the password is not supported, return silently */
+ if(!password) {
+ break;
+ }
+
+ bool pass_valid = (slix->flags & flag_valid);
+ if(!(slix->flags & flag_set)) {
+ pass_valid = true;
+ }
+
+ if(pass_valid) {
+ slix->flags |= flag_valid;
+ slix->flags |= flag_set;
+
+ memcpy(password, new_password, 4);
+
+ ctx->response_buffer[0] = NFCV_NOERROR;
+ nfcv_emu_send(
+ tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
+ snprintf(
+ nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK");
+ } else {
+ snprintf(
+ nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL");
+ }
+ handled = true;
+ break;
+ }
+
case NFCV_CMD_NXP_ENABLE_PRIVACY: {
ctx->response_buffer[0] = NFCV_NOERROR;
@@ -278,7 +480,7 @@ bool slix_generic_protocol_filter(
sizeof(nfcv_data->last_command),
"NFCV_CMD_NXP_ENABLE_PRIVACY");
- slix->privacy = true;
+ slix->flags |= NfcVSlixDataFlagsPrivacy;
handled = true;
break;
}
@@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
- FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
+ FURI_LOG_D(
+ TAG,
+ " Privacy mode: %s",
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_l_protocol_filter;
@@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
- FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
+ FURI_LOG_D(
+ TAG,
+ " Privacy mode: %s",
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_s_protocol_filter;
@@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
- FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
+ FURI_LOG_D(
+ TAG,
+ " Privacy mode: %s",
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_protocol_filter;
@@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
+ NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
+ NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
+ NfcVSlixData* slix = &nfcv_data->sub_data.slix;
+
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
@@ -396,6 +611,157 @@ bool slix2_protocol_filter( // -V524
return true;
}
+ switch(ctx->command) {
+ /* override WRITE BLOCK for block 79 (16 bit counter) */
+ case NFCV_CMD_WRITE_BLOCK:
+ case NFCV_CMD_WRITE_MULTI_BLOCK: {
+ uint8_t resp_len = 1;
+ uint8_t blocks = 1;
+ uint8_t block = nfcv_data->frame[ctx->payload_offset];
+ uint8_t data_pos = ctx->payload_offset + 1;
+
+ if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
+ blocks = nfcv_data->frame[data_pos] + 1;
+ data_pos++;
+ }
+
+ uint8_t* data = &nfcv_data->frame[data_pos];
+ uint32_t data_len = nfcv_data->block_size * blocks;
+
+ if((block + blocks) <= nfcv_data->block_num &&
+ (data_pos + data_len + 2) == nfcv_data->frame_length) {
+ ctx->response_buffer[0] = NFCV_NOERROR;
+
+ for(int block_num = block; block_num < block + blocks; block_num++) {
+ /* special case, 16-bit counter */
+ if(block_num == 79) {
+ uint32_t dest;
+ uint32_t ctr_old;
+
+ memcpy(&dest, &nfcv_data->frame[data_pos], 4);
+ memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4);
+
+ uint32_t ctr_new = ctr_old;
+ bool allowed = true;
+
+ /* increment counter */
+ if(dest == 1) {
+ ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF);
+
+ /* protection flag set? */
+ if(ctr_old & 0x01000000) {
+ allowed = nfcv_data->sub_data.slix.flags &
+ NfcVSlixDataFlagsValidKeyRead;
+ }
+ } else {
+ ctr_new = dest;
+ allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite;
+ }
+
+ if(allowed) {
+ memcpy(&nfcv_data->data[nfcv_data->block_size * block_num], &ctr_new, 4);
+ } else {
+ /* incorrect read or write password */
+ ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
+ ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
+ resp_len = 2;
+ }
+ } else {
+ memcpy(
+ &nfcv_data->data[nfcv_data->block_size * block_num],
+ &nfcv_data->frame[data_pos],
+ nfcv_data->block_size);
+ }
+ data_pos += nfcv_data->block_size;
+ }
+ nfcv_data->modified = true;
+
+ } else {
+ ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
+ ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
+ resp_len = 2;
+ }
+
+ bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) ||
+ (ctx->addressed || ctx->selected);
+
+ if(respond) {
+ nfcv_emu_send(
+ tx_rx,
+ nfcv_data,
+ ctx->response_buffer,
+ resp_len,
+ ctx->response_flags,
+ ctx->send_time);
+ }
+
+ if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
+ snprintf(
+ nfcv_data->last_command,
+ sizeof(nfcv_data->last_command),
+ "WRITE MULTI BLOCK %d, %d blocks",
+ block,
+ blocks);
+ } else {
+ snprintf(
+ nfcv_data->last_command,
+ sizeof(nfcv_data->last_command),
+ "WRITE BLOCK %d <- %02X %02X %02X %02X",
+ block,
+ data[0],
+ data[1],
+ data[2],
+ data[3]);
+ }
+ handled = true;
+ break;
+ }
+
+ case NFCV_CMD_NXP_READ_SIGNATURE: {
+ uint32_t len = 0;
+ ctx->response_buffer[len++] = NFCV_NOERROR;
+ memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature));
+ len += sizeof(slix->signature);
+
+ nfcv_emu_send(
+ tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
+ snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE");
+
+ handled = true;
+ break;
+ }
+
+ case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: {
+ uint32_t len = 0;
+ uint8_t lock_bits = 0;
+
+ /* convert our internal lock bits format into NXP's */
+ lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0;
+ lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0;
+ lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0;
+ lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0;
+
+ ctx->response_buffer[len++] = NFCV_NOERROR;
+ ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer;
+ ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition;
+ ctx->response_buffer[len++] = lock_bits;
+ ctx->response_buffer[len++] = 0x7F; /* features LSB */
+ ctx->response_buffer[len++] = 0x35; /* features */
+ ctx->response_buffer[len++] = 0; /* features */
+ ctx->response_buffer[len++] = 0; /* features MSB */
+
+ nfcv_emu_send(
+ tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
+ snprintf(
+ nfcv_data->last_command,
+ sizeof(nfcv_data->last_command),
+ "GET_NXP_SYSTEM_INFORMATION");
+
+ handled = true;
+ break;
+ }
+ }
+
return handled;
}
@@ -405,7 +771,10 @@ void slix2_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
- FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
+ FURI_LOG_D(
+ TAG,
+ " Privacy mode: %s",
+ (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix2_protocol_filter;
diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h
index 701fa2f82..67f09e46d 100644
--- a/lib/nfc/protocols/slix.h
+++ b/lib/nfc/protocols/slix.h
@@ -8,19 +8,35 @@
#define NFCV_MANUFACTURER_NXP 0x04
/* ISO15693-3 CUSTOM NXP COMMANDS */
-#define NFCV_CMD_NXP_SET_EAS 0xA2
-#define NFCV_CMD_NXP_RESET_EAS 0xA3
-#define NFCV_CMD_NXP_LOCK_EAS 0xA4
-#define NFCV_CMD_NXP_EAS_ALARM 0xA5
-#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
-#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7
-#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0
-#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
-#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2
-#define NFCV_CMD_NXP_SET_PASSWORD 0xB3
-#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4
-#define NFCV_CMD_NXP_DESTROY 0xB9
-#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA
+typedef enum {
+ NFCV_CMD_NXP_SET_EAS = 0xA2,
+ NFCV_CMD_NXP_RESET_EAS = 0xA3,
+ NFCV_CMD_NXP_LOCK_EAS = 0xA4,
+ NFCV_CMD_NXP_EAS_ALARM = 0xA5,
+ NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6,
+ NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7,
+ NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB,
+ NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0,
+ NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1,
+ NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2,
+ NFCV_CMD_NXP_SET_PASSWORD = 0xB3,
+ NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4,
+ NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5,
+ NFCV_CMD_NXP_PROTECT_PAGE = 0xB6,
+ NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7,
+ NFCV_CMD_NXP_DESTROY = 0xB9,
+ NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA,
+ NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC,
+ NFCV_CMD_NXP_READ_SIGNATURE = 0xBD
+} SlixCommands;
+
+/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */
+typedef enum {
+ SlixLockBitAfi = 1 << 0,
+ SlixLockBitEas = 1 << 1,
+ SlixLockBitDsfid = 1 << 2,
+ SlixLockBitPpl = 1 << 3,
+} SlixLockBits;
/* available passwords */
#define SLIX_PASS_READ 0x01
@@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data);
+ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
+ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
+ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
+
ReturnCode slix_get_random(NfcVData* data);
ReturnCode slix_unlock(NfcVData* data, uint32_t password_id);
From b92f7c669b7a166a62b587e62b90f5a4ec9e0378 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sat, 24 Jun 2023 22:11:25 +0300
Subject: [PATCH 12/19] Update changelog
---
CHANGELOG.md | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4249c94f..04f95fbee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
## New changes
-* Infrared: Updated universal remote asstes (by @amec0e | PR #522)
+* Plugins: Fix furi_hal_bus issues in AVR Programmer and Signal Generator (fixes issue #525)
+* Plugins: USB / BLE Remote - Updated UI in keynote vertical and numpad (by @gid9798 | PR #524)
+* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
* Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app
+* Infrared: Updated universal remote asstes (by @amec0e | PR #522)
+* OFW PR 2783: SLIX2 emulation support / practical use for Dymo printers (by @g3gg0)
+* OFW PR 2782: NFC: Fix key invalidation logic (by @AloneLiberty)
* OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed
----
From fef90f1ec545dddd43e8a0731deb81aad4b77d7d Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 00:31:08 +0300
Subject: [PATCH 13/19] Unitemp SCD40 support
by divinebird
---
applications/external/unitemp/Sensors.c | 2 +-
.../external/unitemp/interfaces/endianness.h | 60 ++++
applications/external/unitemp/sensors/SCD30.c | 53 +---
applications/external/unitemp/sensors/SCD40.c | 291 ++++++++++++++++++
applications/external/unitemp/sensors/SCD40.h | 59 ++++
5 files changed, 412 insertions(+), 53 deletions(-)
create mode 100644 applications/external/unitemp/interfaces/endianness.h
create mode 100644 applications/external/unitemp/sensors/SCD40.c
create mode 100644 applications/external/unitemp/sensors/SCD40.h
diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c
index ae90ce4d5..f96401ea2 100644
--- a/applications/external/unitemp/Sensors.c
+++ b/applications/external/unitemp/Sensors.c
@@ -79,7 +79,7 @@ static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT
&Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10,
&SHT30, &GXHT30, &LM75, &HDC1080, &BMP180,
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675,
- &SCD30};
+ &SCD30, &SCD40};
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
if(index > SENSOR_TYPES_COUNT) return NULL;
diff --git a/applications/external/unitemp/interfaces/endianness.h b/applications/external/unitemp/interfaces/endianness.h
new file mode 100644
index 000000000..c4a3f4b87
--- /dev/null
+++ b/applications/external/unitemp/interfaces/endianness.h
@@ -0,0 +1,60 @@
+//
+// Created by Avilov Vasily on 10.06.2023.
+//
+
+#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H
+#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H
+
+inline static void store16(uint8_t* b, uint16_t i) {
+ memcpy(b, &i, 2);
+}
+
+inline static void store32(uint8_t* b, uint32_t i) {
+ memcpy(b, &i, 4);
+}
+
+inline static uint16_t load16(uint8_t* b) {
+ uint16_t x;
+ memcpy(&x, b, 2);
+ return x;
+}
+
+inline static uint32_t load32(uint8_t* b) {
+ uint32_t x;
+ memcpy(&x, b, 4);
+ return x;
+}
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define htobe16(x) (x)
+#define htobe32(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+#define htole32(x) __builtin_bswap32(x)
+#define be16toh(x) (x)
+#define be32toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+#define le32toh(x) __builtin_bswap32(x)
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define htobe16(x) __builtin_bswap16(x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define be16toh(x) __builtin_bswap16(x)
+#define be32toh(x) __builtin_bswap32(x)
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+#else
+#error "What kind of system is this?"
+#endif
+
+#define load16_le(b) (le16toh(load16(b)))
+#define load32_le(b) (le32toh(load32(b)))
+#define store16_le(b, i) (store16(b, htole16(i)))
+#define store32_le(b, i) (store32(b, htole32(i)))
+
+#define load16_be(b) (be16toh(load16(b)))
+#define load32_be(b) (be32toh(load32(b)))
+#define store16_be(b, i) (store16(b, htobe16(i)))
+#define store32_be(b, i) (store32(b, htobe32(i)))
+
+#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H
diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c
index 627130da7..d7a10149c 100644
--- a/applications/external/unitemp/sensors/SCD30.c
+++ b/applications/external/unitemp/sensors/SCD30.c
@@ -21,60 +21,9 @@
#include "SCD30.h"
#include "../interfaces/I2CSensor.h"
+#include "../interfaces/endianness.h"
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
-inline static uint16_t load16(uint8_t* b) {
- uint16_t x;
- memcpy(&x, b, 2);
- return x;
-}
-
-inline static uint32_t load32(uint8_t* b) {
- uint32_t x;
- memcpy(&x, b, 4);
- return x;
-}
-
-inline static void store16(uint8_t* b, uint16_t i) {
- memcpy(b, &i, 2);
-}
-
-inline static void store32(uint8_t* b, uint32_t i) {
- memcpy(b, &i, 4);
-}
-
-#if BYTE_ORDER == BIG_ENDIAN
-#define htobe16(x) (x)
-#define htobe32(x) (x)
-#define htole16(x) __builtin_bswap16(x)
-#define htole32(x) __builtin_bswap32(x)
-#define be16toh(x) (x)
-#define be32toh(x) (x)
-#define le16toh(x) __builtin_bswap16(x)
-#define le32toh(x) __builtin_bswap32(x)
-#elif BYTE_ORDER == LITTLE_ENDIAN
-#define htobe16(x) __builtin_bswap16(x)
-#define htobe32(x) __builtin_bswap32(x)
-#define htole16(x) (x)
-#define htole32(x) (x)
-#define be16toh(x) __builtin_bswap16(x)
-#define be32toh(x) __builtin_bswap32(x)
-#define le16toh(x) (x)
-#define le32toh(x) (x)
-#else
-#error "What kind of system is this?"
-#endif
-
-#define load16_le(b) (le16toh(load16(b)))
-#define load32_le(b) (le32toh(load32(b)))
-#define store16_le(b, i) (store16(b, htole16(i)))
-#define store32_le(b, i) (store32(b, htole32(i)))
-
-#define load16_be(b) (be16toh(load16(b)))
-#define load32_be(b) (be32toh(load32(b)))
-#define store16_be(b, i) (store16(b, htobe16(i)))
-#define store32_be(b, i) (store32(b, htobe32(i)))
-
typedef union {
uint16_t array16[2];
uint8_t array8[4];
diff --git a/applications/external/unitemp/sensors/SCD40.c b/applications/external/unitemp/sensors/SCD40.c
new file mode 100644
index 000000000..c88943a00
--- /dev/null
+++ b/applications/external/unitemp/sensors/SCD40.c
@@ -0,0 +1,291 @@
+/*
+ Unitemp - Universal temperature reader
+ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
+ Contributed by divinebird (https://github.com/divinebird)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
+
+#include "SCD30.h"
+#include "../interfaces/I2CSensor.h"
+#include "../interfaces/endianness.h"
+//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
+
+bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
+bool unitemp_SCD40_init(Sensor* sensor);
+bool unitemp_SCD40_deinit(Sensor* sensor);
+UnitempStatus unitemp_SCD40_update(Sensor* sensor);
+bool unitemp_SCD40_free(Sensor* sensor);
+
+const SensorType SCD40 = {
+ .typename = "SCD40",
+ .interface = &I2C,
+ .datatype = UT_DATA_TYPE_TEMP_HUM_CO2,
+ .pollingInterval = 5000,
+ .allocator = unitemp_SCD40_alloc,
+ .mem_releaser = unitemp_SCD40_free,
+ .initializer = unitemp_SCD40_init,
+ .deinitializer = unitemp_SCD40_deinit,
+ .updater = unitemp_SCD40_update};
+
+#define SCD40_ID 0x62
+
+#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1
+#define COMMAND_READ_MEASUREMENT 0XEC05
+#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86
+
+#define COMMAND_PERSIST_SETTINGS 0X3615
+#define COMMAND_GET_SERIAL_NUMBER 0X3682
+#define COMMAND_PERFORM_SELF_TEST 0X3639
+#define COMMAND_PERFORM_FACTORY_RESET 0X3632
+#define COMMAND_REINIT 0X3646
+
+#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D
+#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318
+#define COMMAND_SET_SENSOR_ALTITUDE 0X2427
+#define COMMAND_GET_SENSOR_ALTITUDE 0X2322
+#define COMMAND_SET_AMBIENT_PRESSURE 0XE000
+#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F
+#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416
+#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313
+
+static bool readMeasurement(Sensor* sensor) __attribute__((unused));
+static void reset(Sensor* sensor) __attribute__((unused));
+
+static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused));
+static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused));
+
+static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused));
+
+static float getTemperatureOffset(Sensor* sensor) __attribute__((unused));
+static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused));
+
+static bool beginMeasuring(Sensor* sensor) __attribute__((unused));
+static bool stopMeasurement(Sensor* sensor) __attribute__((unused));
+
+bool unitemp_SCD40_alloc(Sensor* sensor, char* args) {
+ UNUSED(args);
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+
+ i2c_sensor->minI2CAdr = SCD40_ID << 1;
+ i2c_sensor->maxI2CAdr = SCD40_ID << 1;
+ return true;
+}
+
+bool unitemp_SCD40_free(Sensor* sensor) {
+ //ΠΠ΅ΡΠ΅Π³ΠΎ Π²ΡΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π°ΡΡ, ΡΠ°ΠΊ ΠΊΠ°ΠΊ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π±ΡΠ»ΠΎ Π²ΡΠ΄Π΅Π»Π΅Π½ΠΎ
+ UNUSED(sensor);
+ return true;
+}
+
+bool unitemp_SCD40_init(Sensor* sensor) {
+ return beginMeasuring(sensor);
+}
+
+bool unitemp_SCD40_deinit(Sensor* sensor) {
+ return stopMeasurement(sensor);
+}
+
+UnitempStatus unitemp_SCD40_update(Sensor* sensor) {
+ readMeasurement(sensor);
+ return UT_SENSORSTATUS_OK;
+}
+
+#define CRC8_POLYNOMIAL 0x31
+#define CRC8_INIT 0xFF
+
+static uint8_t computeCRC8(uint8_t* message, uint8_t len) {
+ uint8_t crc = CRC8_INIT; // Init with 0xFF
+ for(uint8_t x = 0; x < len; x++) {
+ crc ^= message[x]; // XOR-in the next input byte
+ for(uint8_t i = 0; i < 8; i++) {
+ if((crc & 0x80) != 0)
+ crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL);
+ else
+ crc <<= 1;
+ }
+ }
+ return crc; // No output reflection
+}
+
+// Sends a command along with arguments and CRC
+static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) {
+ static const uint8_t cmdSize = 5;
+
+ uint8_t bytes[cmdSize];
+ uint8_t* pointer = bytes;
+ store16_be(pointer, command);
+ pointer += 2;
+ uint8_t* argPos = pointer;
+ store16_be(pointer, arguments);
+ pointer += 2;
+ *pointer = computeCRC8(argPos, pointer - argPos);
+
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
+}
+
+// Sends just a command, no arguments, no CRC
+static bool sendCommand(Sensor* sensor, uint16_t command) {
+ static const uint8_t cmdSize = 2;
+
+ uint8_t bytes[cmdSize];
+ store16_be(bytes, command);
+
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
+}
+
+static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) {
+ static const uint8_t regSize = 2;
+
+ if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK
+
+ furi_delay_ms(3);
+
+ uint8_t bytes[regSize];
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0;
+
+ return load16_be(bytes);
+}
+
+static bool loadWord(uint8_t* buff, uint16_t* val) {
+ uint16_t tmp = load16_be(buff);
+ uint8_t expectedCRC = computeCRC8(buff, 2);
+ if(buff[2] != expectedCRC) return false;
+ *val = tmp;
+ return true;
+}
+
+static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) {
+ static const uint8_t respSize = 3;
+
+ if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK
+
+ furi_delay_ms(3);
+
+ uint8_t bytes[respSize];
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false;
+
+ return loadWord(bytes, val);
+}
+
+// Get 18 bytes from SCD30
+// Updates global variables with floats
+// Returns true if success
+static bool readMeasurement(Sensor* sensor) {
+ if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
+ FURI_LOG_E(APP_NAME, "Sensor did not ACK");
+ return false; // Sensor did not ACK
+ }
+
+ furi_delay_ms(3);
+
+ static const uint8_t respSize = 9;
+ uint8_t buff[respSize];
+ uint8_t* bytes = buff;
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
+ FURI_LOG_E(APP_NAME, "Error while read measures");
+ return false;
+ }
+
+ uint16_t tmpValue;
+
+ bool error = false;
+ if(loadWord(bytes, &tmpValue)) {
+ sensor->co2 = tmpValue;
+ } else {
+ FURI_LOG_E(APP_NAME, "Error while parsing CO2");
+ error = true;
+ }
+
+ bytes += 3;
+ if(loadWord(bytes, &tmpValue)) {
+ sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f;
+ } else {
+ FURI_LOG_E(APP_NAME, "Error while parsing temp");
+ error = true;
+ }
+
+ bytes += 3;
+ if(loadWord(bytes, &tmpValue)) {
+ sensor->hum = (float)tmpValue * 100.0f / 65535.0f;
+ } else {
+ FURI_LOG_E(APP_NAME, "Error while parsing humidity");
+ error = true;
+ }
+
+ return !error;
+}
+
+static void reset(Sensor* sensor) {
+ sendCommand(sensor, COMMAND_REINIT);
+}
+
+static bool setAutoSelfCalibration(Sensor* sensor, bool enable) {
+ return sendCommandWithCRC(
+ sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC
+}
+
+// Get the current ASC setting
+static bool getAutoSelfCalibration(Sensor* sensor) {
+ return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED);
+}
+
+// Unfinished
+static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) {
+ if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
+ FURI_LOG_E(APP_NAME, "Sensor did not ACK");
+ return false; // Sensor did not ACK
+ }
+
+ static const uint8_t respSize = 9;
+ uint8_t buff[respSize];
+ uint8_t* bytes = buff;
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
+ FURI_LOG_E(APP_NAME, "Error while read measures");
+ return false;
+ }
+
+ *val = 0;
+
+ return true;
+}
+
+static bool beginMeasuring(Sensor* sensor) {
+ return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT);
+}
+
+// Stop continuous measurement
+static bool stopMeasurement(Sensor* sensor) {
+ return sendCommand(sensor, COMMAND_READ_MEASUREMENT);
+}
+
+static float getTemperatureOffset(Sensor* sensor) {
+ uint16_t curOffset;
+ if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0;
+ return (float)curOffset * 175.0f / 65536.0f;
+}
+
+static bool setTemperatureOffset(Sensor* sensor, float tempOffset) {
+ uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f;
+ return sendCommandWithCRC(
+ sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC
+}
diff --git a/applications/external/unitemp/sensors/SCD40.h b/applications/external/unitemp/sensors/SCD40.h
new file mode 100644
index 000000000..5cf7a4324
--- /dev/null
+++ b/applications/external/unitemp/sensors/SCD40.h
@@ -0,0 +1,59 @@
+/*
+ Unitemp - Universal temperature reader
+ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
+ Contributed by divinebird (https://github.com/divinebird)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+#ifndef UNITEMP_SCD40
+#define UNITEMP_SCD40
+
+#include "../unitemp.h"
+#include "../Sensors.h"
+
+extern const SensorType SCD40;
+/**
+ * @brief ΠΡΠ΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΌΡΡΠΈ ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π½Π°ΡΠ°Π»ΡΠ½ΡΡ
Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ Π΄Π°ΡΡΠΈΠΊΠ° SCD40
+ * @param sensor Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° ΡΠΎΠ·Π΄Π°Π²Π°Π΅ΠΌΡΠΉ Π΄Π°ΡΡΠΈΠΊ
+ * @return ΠΡΡΠΈΠ½Π° ΠΏΡΠΈ ΡΡΠΏΠ΅Ρ
Π΅
+ */
+bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
+
+/**
+ * @brief ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ Π΄Π°ΡΡΠΈΠΊΠ° SCD40
+ * @param sensor Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° Π΄Π°ΡΡΠΈΠΊ
+ * @return ΠΡΡΠΈΠ½Π° Π΅ΡΠ»ΠΈ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠΏΡΠΏΠ΅ΡΠ½Π°Ρ
+ */
+bool unitemp_SCD40_init(Sensor* sensor);
+
+/**
+ * @brief ΠΠ΅ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π΄Π°ΡΡΠΈΠΊΠ°
+ * @param sensor Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° Π΄Π°ΡΡΠΈΠΊ
+ */
+bool unitemp_SCD40_deinit(Sensor* sensor);
+
+/**
+ * @brief ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΠΈΠ· Π΄Π°ΡΡΠΈΠΊΠ°
+ * @param sensor Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° Π΄Π°ΡΡΠΈΠΊ
+ * @return Π‘ΡΠ°ΡΡΡ ΠΎΠΏΡΠΎΡΠ° Π΄Π°ΡΡΠΈΠΊΠ°
+ */
+UnitempStatus unitemp_SCD40_update(Sensor* sensor);
+
+/**
+ * @brief ΠΡΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡΡ ΠΏΠ°ΠΌΡΡΡ Π΄Π°ΡΡΠΈΠΊΠ°
+ * @param sensor Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° Π΄Π°ΡΡΠΈΠΊ
+ */
+bool unitemp_SCD40_free(Sensor* sensor);
+
+#endif
\ No newline at end of file
From 34ff4c7dfa3354c7eff9004ae11256ec4967b8c0 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 00:31:23 +0300
Subject: [PATCH 14/19] .
---
applications/external/unitemp/Sensors.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h
index 25b9cb49e..aec220c0e 100644
--- a/applications/external/unitemp/Sensors.h
+++ b/applications/external/unitemp/Sensors.h
@@ -334,4 +334,5 @@ const GPIO*
#include "./sensors/MAX31855.h"
#include "./sensors/MAX6675.h"
#include "./sensors/SCD30.h"
+#include "./sensors/SCD40.h"
#endif
From 35f95336ed24142de1fc5b8068e1e4eda3d91832 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 00:38:26 +0300
Subject: [PATCH 15/19] Heat index
by ClementGre
---
applications/external/unitemp/Sensors.c | 13 +++--
applications/external/unitemp/Sensors.h | 1 +
.../unitemp/assets/heat_index_11x14.png | Bin 0 -> 1239 bytes
applications/external/unitemp/unitemp.c | 30 +++++++++++
applications/external/unitemp/unitemp.h | 11 +++-
.../external/unitemp/views/General_view.c | 50 +++++++++++++++---
.../external/unitemp/views/Settings_view.c | 15 ++++++
7 files changed, 109 insertions(+), 11 deletions(-)
create mode 100644 applications/external/unitemp/assets/heat_index_11x14.png
diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c
index f96401ea2..33dd3fa88 100644
--- a/applications/external/unitemp/Sensors.c
+++ b/applications/external/unitemp/Sensors.c
@@ -624,11 +624,16 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) {
UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status);
}
- if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) {
- uintemp_celsiumToFarengate(sensor);
- }
-
if(sensor->status == UT_SENSORSTATUS_OK) {
+ if(app->settings.heat_index &&
+ ((sensor->type->datatype & (UT_TEMPERATURE | UT_HUMIDITY)) ==
+ (UT_TEMPERATURE | UT_HUMIDITY))) {
+ unitemp_calculate_heat_index(sensor);
+ }
+ if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT) {
+ uintemp_celsiumToFarengate(sensor);
+ }
+
sensor->temp += sensor->temp_offset / 10.f;
if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) {
unitemp_pascalToMmHg(sensor);
diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h
index aec220c0e..339a4deff 100644
--- a/applications/external/unitemp/Sensors.h
+++ b/applications/external/unitemp/Sensors.h
@@ -119,6 +119,7 @@ typedef struct Sensor {
char* name;
//Π’Π΅ΠΌΠΏΠ΅ΡΠ°ΡΡΡΠ°
float temp;
+ float heat_index;
//ΠΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½Π°Ρ Π²Π»Π°ΠΆΠ½ΠΎΡΡΡ
float hum;
//ΠΡΠΌΠΎΡΡΠ΅ΡΠ½ΠΎΠ΅ Π΄Π°Π²Π»Π΅Π½ΠΈΠ΅
diff --git a/applications/external/unitemp/assets/heat_index_11x14.png b/applications/external/unitemp/assets/heat_index_11x14.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2f0d4ce5ffe1ca23daf24e02f7d250072560913
GIT binary patch
literal 1239
zcmeAS@N?(olHy`uVBq!ia0vp^+(697!2~4Bm=@peSF#SkG9`3@FF6fC;XTdjT_?%?FfY&|GmkA4qW)ctjQhU48_F8K-LV
zNi#4o=VXRNltlRYSS9D@>LsS+C#C9DzC
zHb_`sNdc^+B->Ug!Z$#{Ilm}X!A#FU&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`Ml
zFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uO
zWNuzUS^4%5mXDB
zFuJZtggiD2k)_eK`WI!U0uv+Ht-yc=I}lk6s@_H)lpc}NCnWKMMS-c`jtdwpu$*Ma
zRnxte2bj6CJzX3_B&L=IT;yX=temp = sensor->temp * (9.0 / 5.0) + 32;
+ sensor->heat_index = sensor->heat_index * (9.0 / 5.0) + 32;
}
+static float heat_index_consts[9] = {
+ -42.379f,
+ 2.04901523f,
+ 10.14333127f,
+ -0.22475541f,
+ -0.00683783f,
+ -0.05481717f,
+ 0.00122874f,
+ 0.00085282f,
+ -0.00000199f};
+void unitemp_calculate_heat_index(Sensor* sensor) {
+ // temp should be in Celsius, heat index will be in Celsius
+ float temp = sensor->temp * (9.0 / 5.0) + 32.0f;
+ float hum = sensor->hum;
+ sensor->heat_index =
+ (heat_index_consts[0] + heat_index_consts[1] * temp + heat_index_consts[2] * hum +
+ heat_index_consts[3] * temp * hum + heat_index_consts[4] * temp * temp +
+ heat_index_consts[5] * hum * hum + heat_index_consts[6] * temp * temp * hum +
+ heat_index_consts[7] * temp * hum * hum + heat_index_consts[8] * temp * temp * hum * hum -
+ 32.0f) *
+ (5.0 / 9.0);
+}
void unitemp_pascalToMmHg(Sensor* sensor) {
sensor->pressure = sensor->pressure * 0.007500638;
}
@@ -71,6 +94,7 @@ bool unitemp_saveSettings(void) {
app->file_stream, "INFINITY_BACKLIGHT %d\n", app->settings.infinityBacklight);
stream_write_format(app->file_stream, "TEMP_UNIT %d\n", app->settings.temp_unit);
stream_write_format(app->file_stream, "PRESSURE_UNIT %d\n", app->settings.pressure_unit);
+ stream_write_format(app->file_stream, "HEAT_INDEX %d\n", app->settings.heat_index);
//ΠΠ°ΠΊΡΡΡΠΈΠ΅ ΠΏΠΎΡΠΎΠΊΠ° ΠΈ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΌΡΡΠΈ
file_stream_close(app->file_stream);
@@ -166,6 +190,11 @@ bool unitemp_loadSettings(void) {
int p = 0;
sscanf(((char*)(file_buf + line_end)), "\nPRESSURE_UNIT %d", &p);
app->settings.pressure_unit = p;
+ } else if(!strcmp(buff, "HEAT_INDEX")) {
+ //Π§ΡΠ΅Π½ΠΈΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°
+ int p = 0;
+ sscanf(((char*)(file_buf + line_end)), "\nHEAT_INDEX %d", &p);
+ app->settings.heat_index = p;
} else {
FURI_LOG_W(APP_NAME, "Unknown settings parameter: %s", buff);
}
@@ -203,6 +232,7 @@ static bool unitemp_alloc(void) {
app->settings.infinityBacklight = true; //ΠΠΎΠ΄ΡΠ²Π΅ΡΠΊΠ° Π³ΠΎΡΠΈΡ Π²ΡΠ΅Π³Π΄Π°
app->settings.temp_unit = UT_TEMP_CELSIUS; //ΠΠ΄ΠΈΠ½ΠΈΡΠ° ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΡΠ΅ΠΌΠΏΠ΅ΡΠ°ΡΡΡΡ - Π³ΡΠ°Π΄ΡΡΡ Π¦Π΅Π»ΡΡΠΈΡ
app->settings.pressure_unit = UT_PRESSURE_MM_HG; //ΠΠ΄ΠΈΠ½ΠΈΡΠ° ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π°Π²Π»Π΅Π½ΠΈΡ - ΠΌΠΌ ΡΡ. ΡΡ.
+ app->settings.heat_index = false;
app->gui = furi_record_open(RECORD_GUI);
//ΠΠΈΡΠΏΠ΅ΡΡΠ΅Ρ ΠΎΠΊΠΎΠ½
diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h
index 69cd8cf4f..c2b61b899 100644
--- a/applications/external/unitemp/unitemp.h
+++ b/applications/external/unitemp/unitemp.h
@@ -40,7 +40,7 @@
//ΠΠΌΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ
#define APP_NAME "Unitemp"
//ΠΠ΅ΡΡΠΈΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ
-#define UNITEMP_APP_VER "1.3"
+#define UNITEMP_APP_VER "1.4"
//ΠΡΡΡ Ρ
ΡΠ°Π½Π΅Π½ΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² ΠΏΠ»Π°Π³ΠΈΠ½Π°
#define APP_PATH_FOLDER "/ext/unitemp"
//ΠΠΌΡ ΡΠ°ΠΉΠ»Π° Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°ΠΌΠΈ
@@ -80,6 +80,8 @@ typedef struct {
tempMeasureUnit temp_unit;
//ΠΠ΄ΠΈΠ½ΠΈΡΠ° ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π°Π²Π»Π΅Π½ΠΈΡ
pressureMeasureUnit pressure_unit;
+ // Do calculate and show heat index
+ bool heat_index;
//ΠΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ OTG
bool lastOTGState;
} UnitempSettings;
@@ -111,6 +113,13 @@ typedef struct {
/* ΠΠ±ΡΡΠ²Π»Π΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠΎΡΠΈΠΏΠΎΠ² ΡΡΠ½ΠΊΡΠΈΠΉ */
+/**
+ * @brief Calculates the heat index in Celsius from the temperature and humidity and stores it in the sensor heat_index field
+ *
+ * @param sensor The sensor struct, with temperature in Celcius and humidity in percent
+ */
+void unitemp_calculate_heat_index(Sensor* sensor);
+
/**
* @brief ΠΠ΅ΡΠ΅Π²ΠΎΠ΄ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΡΠ΅ΠΌΠΏΠ΅ΡΠ°ΡΡΡΡ Π΄Π°ΡΡΠΈΠΊΠ° ΠΈΠ· Π¦Π΅Π»ΡΡΠΈΡ Π² Π€Π°ΡΠ΅Π½Π³Π΅ΠΉΡΡ
*
diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c
index 22d724935..13e98715a 100644
--- a/applications/external/unitemp/views/General_view.c
+++ b/applications/external/unitemp/views/General_view.c
@@ -113,6 +113,33 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2])
canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%");
}
+static void _draw_heat_index(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) {
+ canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3);
+ canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3);
+
+ canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 3, &I_heat_index_11x14);
+
+ int16_t heat_index_int = sensor->heat_index;
+ int8_t heat_index_dec = abs((int16_t)(sensor->heat_index * 10) % 10);
+
+ snprintf(app->buff, BUFF_SIZE, "%d", heat_index_int);
+ canvas_set_font(canvas, FontBigNumbers);
+ canvas_draw_str_aligned(
+ canvas,
+ pos[0] + 27 + ((sensor->heat_index <= -10 || sensor->heat_index > 99) ? 5 : 0),
+ pos[1] + 10,
+ AlignCenter,
+ AlignCenter,
+ app->buff);
+
+ if(heat_index_int <= 99) {
+ uint8_t int_len = canvas_string_width(canvas, app->buff);
+ snprintf(app->buff, BUFF_SIZE, ".%d", heat_index_dec);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 2, pos[1] + 10 + 7, app->buff);
+ }
+}
+
static void _draw_pressure(Canvas* canvas, Sensor* sensor) {
const uint8_t x = 29, y = 39;
//Π ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠ°ΠΌΠΊΠΈ
@@ -320,12 +347,23 @@ static void _draw_carousel_values(Canvas* canvas) {
ColorWhite);
break;
case UT_DATA_TYPE_TEMP_HUM:
- _draw_temperature(
- canvas,
- unitemp_sensor_getActive(generalview_sensor_index),
- temp_positions[1][0],
- temp_positions[1][1],
- ColorWhite);
+ if(!app->settings.heat_index) {
+ _draw_temperature(
+ canvas,
+ unitemp_sensor_getActive(generalview_sensor_index),
+ temp_positions[1][0],
+ temp_positions[1][1],
+ ColorWhite);
+ } else {
+ _draw_temperature(
+ canvas,
+ unitemp_sensor_getActive(generalview_sensor_index),
+ temp_positions[2][0],
+ temp_positions[2][1],
+ ColorWhite);
+ _draw_heat_index(
+ canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]);
+ }
_draw_humidity(
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[0]);
break;
diff --git a/applications/external/unitemp/views/Settings_view.c b/applications/external/unitemp/views/Settings_view.c
index e61c6cad6..3d1eca906 100644
--- a/applications/external/unitemp/views/Settings_view.c
+++ b/applications/external/unitemp/views/Settings_view.c
@@ -26,6 +26,7 @@ static VariableItemList* variable_item_list;
static const char states[2][9] = {"Auto", "Infinity"};
static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"};
static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"};
+static const char heat_index_bool[2][4] = {"OFF", "ON"};
//ΠΠ»Π΅ΠΌΠ΅Π½Ρ ΡΠΏΠΈΡΠΊΠ° - Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½Π°Ρ ΠΏΠΎΠ΄ΡΠ²Π΅ΡΠΊΠ°
VariableItem* infinity_backlight_item;
@@ -33,6 +34,8 @@ VariableItem* infinity_backlight_item;
VariableItem* temperature_unit_item;
//ΠΠ΄ΠΈΠ½ΠΈΡΠ° ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π°Π²Π»Π΅Π½ΠΈΡ
VariableItem* pressure_unit_item;
+
+VariableItem* heat_index_item;
#define VIEW_ID UnitempViewSettings
/**
@@ -57,6 +60,7 @@ static uint32_t _exit_callback(void* context) {
(bool)variable_item_get_current_value_index(infinity_backlight_item);
app->settings.temp_unit = variable_item_get_current_value_index(temperature_unit_item);
app->settings.pressure_unit = variable_item_get_current_value_index(pressure_unit_item);
+ app->settings.heat_index = variable_item_get_current_value_index(heat_index_item);
unitemp_saveSettings();
unitemp_loadSettings();
@@ -90,6 +94,11 @@ static void _setting_change_callback(VariableItem* item) {
pressure_unit_item,
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
}
+ if(item == heat_index_item) {
+ variable_item_set_current_value_text(
+ heat_index_item,
+ heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
+ }
}
/**
@@ -106,6 +115,8 @@ void unitemp_Settings_alloc(void) {
variable_item_list_add(variable_item_list, "Temp. unit", 2, _setting_change_callback, app);
pressure_unit_item = variable_item_list_add(
variable_item_list, "Press. unit", UT_PRESSURE_COUNT, _setting_change_callback, app);
+ heat_index_item = variable_item_list_add(
+ variable_item_list, "Calc. heat index", 2, _setting_change_callback, app);
//ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π±Π΅ΠΊΠ° Π½Π° Π½Π°ΠΆΠ°ΡΠΈΠ΅ ΡΡΠ΅Π΄Π½Π΅ΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΈ
variable_item_list_set_enter_callback(variable_item_list, _enter_callback, app);
@@ -139,6 +150,10 @@ void unitemp_Settings_switch(void) {
pressure_unit_item,
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
+ variable_item_set_current_value_index(heat_index_item, (uint8_t)app->settings.heat_index);
+ variable_item_set_current_value_text(
+ heat_index_item, heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
+
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID);
}
From 300bd2c16bba670377a7d314a55a200726bc8f4c Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 00:44:15 +0300
Subject: [PATCH 16/19] Update changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04f95fbee..846ead301 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
## New changes
+* Plugins: UniTemp update merged PRs -> Heat Index Feature (by @ClementGre) + Append carbon dioxide sensor (SCD40) (by @divinebird)
* Plugins: Fix furi_hal_bus issues in AVR Programmer and Signal Generator (fixes issue #525)
* Plugins: USB / BLE Remote - Updated UI in keynote vertical and numpad (by @gid9798 | PR #524)
* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
From eb282d20b771b7426140c8f16caec74fc463cf91 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 00:53:36 +0300
Subject: [PATCH 17/19] Fix numpad ui wrong placed message
---
applications/external/hid_app/views/hid_numpad.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c
index c27576862..bd4788b83 100644
--- a/applications/external/hid_app/views/hid_numpad.c
+++ b/applications/external/hid_app/views/hid_numpad.c
@@ -140,11 +140,11 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
+ elements_multiline_text_aligned(
+ canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
}
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
- elements_multiline_text_aligned(
- canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
} else {
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
}
From 190d47e5280823b327bcbea19ad181a85fa4a921 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 01:39:46 +0300
Subject: [PATCH 18/19] Unitemp Fix SDA SCL pin numbers text
---
applications/external/unitemp/views/General_view.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c
index 13e98715a..0a8e8ad17 100644
--- a/applications/external/unitemp/views/General_view.c
+++ b/applications/external/unitemp/views/General_view.c
@@ -484,8 +484,8 @@ static void _draw_carousel_info(Canvas* canvas) {
->currentI2CAdr >>
1);
canvas_draw_str(canvas, 57, 35, app->buff);
- canvas_draw_str(canvas, 54, 46, "15 (C0)");
- canvas_draw_str(canvas, 54, 58, "16 (C1)");
+ canvas_draw_str(canvas, 54, 46, "15 (C1)");
+ canvas_draw_str(canvas, 54, 58, "16 (C0)");
}
}
static void _draw_view_sensorsCarousel(Canvas* canvas) {
From 7aa15ada3002893d2a73a2c046bbdd44aa97f7fe Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 25 Jun 2023 03:10:28 +0300
Subject: [PATCH 19/19] merge fix
---
applications/main/application.fam | 1 -
1 file changed, 1 deletion(-)
diff --git a/applications/main/application.fam b/applications/main/application.fam
index 6fa239123..c8884f934 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -31,7 +31,6 @@ App(
"subghz",
#"bad_usb",
#"u2f",
- "fap_loader",
"archive",
],
)