Merge remote-tracking branch 'upstream/dev' into feat/playlist

This commit is contained in:
Daniel 2022-08-24 15:30:22 +02:00
commit fe7d089593
No known key found for this signature in database
GPG Key ID: A9896FFF5A793A20
333 changed files with 14731 additions and 7111 deletions

View File

@ -44,6 +44,7 @@ steps:
- cp assets/resources/nfc/assets/mf_classic_dict.nfc sd-card/nfc/assets/mf_classic_dict.nfc
- cp assets/resources/infrared/assets/tv.ir sd-card/infrared/assets/tv.ir
- cp assets/resources/infrared/assets/ac.ir sd-card/infrared/assets/ac.ir
- cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir
- cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir
- cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt
- cp assets/resources/Manifest sd-card/Manifest

4
.gitignore vendored
View File

@ -51,3 +51,7 @@ build/
# openocd output file
openocd.log
# PVS Studio temporary files
.PVS-Studio/
PVS-Studio.log

22
.pvsconfig Normal file
View File

@ -0,0 +1,22 @@
# MLib macros we can't do much about.
//-V:M_EACH:1048,1044
//-V:ARRAY_DEF:760,747,568,776,729,712,654
//-V:LIST_DEF:760,747,568,712,729,654,776
//-V:BPTREE_DEF2:779,1086,557,773,512
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
//-V:ALGO_DEF:1048,747,1044
# Non-severe malloc/null pointer deref warnings
//-V::522:2,3
# Warning about headers with copyleft license
//-V::1042
# Potentially null argument warnings
//-V:memset:575
//-V:memcpy:575
//-V:strcpy:575
//-V:strchr:575
# For loop warning on M_FOREACH
//-V:for:1044

1
.pvsoptions Normal file
View File

@ -0,0 +1 @@
--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e */arm-none-eabi/*

View File

@ -1,13 +1,23 @@
### New changes
* Wifi Marauder app update (by @0xchocolate)
* Updated Universal remote assets (by @Amec0e)
* Fixed music player
* Fixed typos in subghz encoders
* OFW: New NFC info screens
* OFW: U2F fixes
* New universal remote for projectors
* OFW: New LF-RFID subsystem (New protocols, Animal tags support)
* Updated universal remote assets (by @Amec0e)
* Renamed UniRF Remix -> Sub-GHz Remote
* Replaced Hex/Dec converter with Multi Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
* New update screen, readme pictures (by @Svaarich)
* Fixed crash if Center button is pressed on the "update success" screen via screensharing
* Temporary disabled one log call in picopass plugin to fix crash/freeze on Read screen
* OFW: Picopass load/info/delete
* OFW: SubGhz: add protocol Magellen
* OFW: Fix mifare ultralight/ntag unlock
* OFW: Dolphin level thresholds update
* OFW: Add MFC 1/4K 4/7bUID to "Add Manually"
* OFW: Other fixes and changes
**Note: Prefer installing using web updater or by self update package, all needed assets will be installed**
**Build naming has been changed - all same as before but `cg - codegrabber` changed to `un - unleashed`**
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip`
DFU for update using qFlipper - `flipper-z-f7-full-(version).dfu`

View File

@ -1,6 +1,8 @@
# Flipper Zero Unleashed Firmware
<img src="https://i.ibb.co/wQ12PVc/fzCUSTOM.png" alt="fzCUSTOM" border="0">
<h3 align="center">
<a href="https://github.com/Eng1n33r/flipperzero-firmware">
<img src="https://user-images.githubusercontent.com/10697207/186202043-26947e28-b1cc-459a-8f20-ffcc7fc0c71c.png" align="center" alt="fzCUSTOM" border="0">
</a>
</h3>
Welcome to Flipper Zero's Custom Firmware repo!
Our goal is to make any features possible in this device without any limitations!
@ -30,6 +32,9 @@ Our Discord Community:
* Picopass/iClass plugin included in releases
* Recompiled IR TV Universal Remote for ALL buttons
* Universal A/C and Audio(soundbars, etc.) remote
* Universal remote for Projectors
* BadUSB keyboard layouts
* Customizable Flipper name
* Other small fixes and changes throughout
See changelog in releases for latest updates!
@ -57,13 +62,12 @@ See changelog in releases for latest updates!
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
- Dec/Hex Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/dec_hex_converter)
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
- WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player)
- UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
- HID Analyzer [(by Ownasaurus)](https://github.com/Ownasaurus/flipperzero-firmware/tree/hid-analyzer/applications/hid_analyzer)
- Simple Clock (fixed) !! New version WIP, wait for updates !! [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- UniversalRF Remix (with changes)(only RAW subghz files) [(by ESurge)(Original UniversalRF by jimilinuxguy)](https://github.com/ESurge/flipperzero-firmware-unirfremix)
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
@ -87,10 +91,12 @@ See changelog in releases for latest updates!
### **Plugins**
## [- Configure UniversalRF Remix App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/UniRFRemix.md)
## [- Configure Sub-GHz Remote App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
## [- Barcode Generator](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md)
## [- Multi Converter](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/MultiConverter.md)
## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme)
### **Plugins that works with external hardware**
@ -142,7 +148,6 @@ See changelog in releases for latest updates!
- `assets` - Assets used by applications and services
- `furi` - Furi Core: os level primitives and helpers
- `debug` - Debug tool: GDB-plugins, SVD-file and etc
- `docker` - Docker image sources (used for firmware build automation)
- `documentation` - Documentation generation system configs and input files
- `firmware` - Firmware source code
- `lib` - Our and 3rd party libraries, drivers and etc...

View File

@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) {
if(app == ArchiveAppTypeU2f) {
bool file_exists = false;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(fs_api);
Storage* storage = furi_record_open(RECORD_STORAGE);
file_exists =
storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) {
storage_file_close(file);
file_exists =
storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) {
storage_file_close(file);
}
if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) {
file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f"));
}
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
return file_exists;
} else {
return false;

View File

@ -77,14 +77,24 @@ static void archive_long_load_cb(void* context) {
});
}
void archive_file_browser_set_callbacks(ArchiveBrowserView* browser) {
static void archive_file_browser_set_path(
ArchiveBrowserView* browser,
string_t path,
const char* filter_ext,
bool skip_assets) {
furi_assert(browser);
file_browser_worker_set_callback_context(browser->worker, browser);
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb);
file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb);
if(!browser->worker_running) {
browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets);
file_browser_worker_set_callback_context(browser->worker, browser);
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb);
file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb);
browser->worker_running = true;
} else {
furi_assert(browser->worker);
file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets);
}
}
bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) {
@ -438,8 +448,8 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
tab = archive_get_tab(browser);
if(archive_is_dir_exists(browser->path)) {
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
file_browser_worker_set_config(
browser->worker, browser->path, archive_get_tab_ext(tab), skip_assets);
archive_file_browser_set_path(
browser, browser->path, archive_get_tab_ext(tab), skip_assets);
tab_empty = false; // Empty check will be performed later
} else {
tab_empty = true;

View File

@ -87,4 +87,3 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key);
void archive_enter_dir(ArchiveBrowserView* browser, string_t name);
void archive_leave_dir(ArchiveBrowserView* browser);
void archive_refresh_dir(ArchiveBrowserView* browser);
void archive_file_browser_set_callbacks(ArchiveBrowserView* browser);

View File

@ -82,9 +82,8 @@ uint16_t archive_favorites_count(void* context) {
static bool archive_favourites_rescan() {
string_t buffer;
string_init(buffer);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(fs_api);
File* fav_item_file = storage_file_alloc(fs_api);
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) {
@ -101,13 +100,8 @@ static bool archive_favourites_rescan() {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
}
} else {
bool file_exists = storage_file_open(
fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) {
storage_file_close(fav_item_file);
if(storage_file_exists(storage, string_get_cstr(buffer))) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
} else {
storage_file_close(fav_item_file);
}
}
}
@ -116,12 +110,11 @@ static bool archive_favourites_rescan() {
string_clear(buffer);
storage_file_close(file);
storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
storage_common_remove(storage, ARCHIVE_FAV_PATH);
storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH);
storage_file_free(file);
storage_file_free(fav_item_file);
furi_record_close(RECORD_STORAGE);
return result;
@ -131,9 +124,8 @@ bool archive_favorites_read(void* context) {
furi_assert(context);
ArchiveBrowserView* browser = context;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(fs_api);
File* fav_item_file = storage_file_alloc(fs_api);
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
string_t buffer;
FileInfo file_info;
@ -163,16 +155,12 @@ bool archive_favorites_read(void* context) {
need_refresh = true;
}
} else {
bool file_exists = storage_file_open(
fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
if(file_exists) {
storage_common_stat(fs_api, string_get_cstr(buffer), &file_info);
storage_file_close(fav_item_file);
if(storage_file_exists(storage, string_get_cstr(buffer))) {
storage_common_stat(storage, string_get_cstr(buffer), &file_info);
archive_add_file_item(
browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer));
file_count++;
} else {
storage_file_close(fav_item_file);
need_refresh = true;
}
}
@ -183,7 +171,6 @@ bool archive_favorites_read(void* context) {
storage_file_close(file);
string_clear(buffer);
storage_file_free(file);
storage_file_free(fav_item_file);
furi_record_close(RECORD_STORAGE);
archive_set_item_count(browser, file_count);

View File

@ -370,18 +370,15 @@ ArchiveBrowserView* browser_alloc() {
return true;
});
browser->worker = file_browser_worker_alloc(browser->path, "*", false);
archive_file_browser_set_callbacks(browser);
file_browser_worker_set_callback_context(browser->worker, browser);
return browser;
}
void browser_free(ArchiveBrowserView* browser) {
furi_assert(browser);
file_browser_worker_free(browser->worker);
if(browser->worker_running) {
file_browser_worker_free(browser->worker);
}
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {

View File

@ -74,6 +74,7 @@ typedef enum {
struct ArchiveBrowserView {
View* view;
BrowserWorker* worker;
bool worker_running;
ArchiveBrowserViewCallback callback;
void* context;
string_t path;

View File

@ -3,7 +3,7 @@
#include <applications/cli/cli.h>
#include <lib/toolbox/args.h>
#include "ble.h"
#include <ble/ble.h>
#include "bt_settings.h"
#include "bt_service/bt.h"

6
applications/bt/bt_hid_app/bt_hid.c Executable file → Normal file
View File

@ -89,8 +89,7 @@ BtHid* bt_hid_app_alloc() {
app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app);
submenu_add_item(
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
submenu_add_item(
app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
view_dispatcher_add_view(
@ -134,7 +133,8 @@ BtHid* bt_hid_app_alloc() {
app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse));
// TODO switch to menu after Media is done
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote);
app->view_id = BtHidViewSubmenu;
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
return app;
}

View File

@ -43,7 +43,10 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
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);
@ -97,8 +100,8 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 110, 49, &I_Ok_btn_9x9);
elements_multiline_text_aligned(canvas, 76, 56, AlignLeft, AlignBottom, "Back");
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
}
static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) {

View File

@ -49,7 +49,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6);
@ -57,7 +59,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6);
@ -65,7 +69,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
@ -74,7 +80,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
@ -89,6 +97,12 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
canvas_draw_line(canvas, 100, 29, 100, 33);
canvas_draw_line(canvas, 102, 29, 102, 33);
canvas_set_color(canvas, ColorBlack);
// Exit
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
}
static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) {

View File

@ -36,7 +36,11 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
canvas_set_font(canvas, FontSecondary);
if(model->left_mouse_held == true) {
elements_multiline_text_aligned(canvas, 0, 60, AlignLeft, AlignBottom, "Selecting...");
elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting...");
} else {
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
}
// Keypad circles
@ -44,7 +48,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9);
@ -52,7 +58,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9);
@ -60,7 +68,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7);
@ -68,7 +78,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7);
@ -76,18 +88,17 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->left_mouse_pressed) {
canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13);
} else {
canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9);
}
canvas_draw_icon(canvas, 83, 27, &I_Ok_btn_9x9);
canvas_set_color(canvas, ColorBlack);
// Back
if(model->right_mouse_pressed) {
canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13);
} else {
canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9);
}
canvas_draw_icon(canvas, 110, 50, &I_Ok_btn_9x9);
}
static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) {

View File

@ -1,404 +0,0 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#define DEC_HEX_CONVERTER_NUMBER_DIGITS 9
#define DEC_HEX_CONVERTER_KEYS 18
#define DEC_HEX_CONVERTER_KEY_DEL 16
// #define DEC_HEX_CONVERTER_KEY_SWAP 17 // actually not used...
#define DEC_HEX_CONVERTER_CHAR_DEL '<'
#define DEC_HEX_CONVERTER_CHAR_SWAP 's'
#define DEC_HEX_CONVERTER_CHAR_MODE '#'
#define DEC_HEX_CONVERTER_CHAR_OVERFLOW '#'
#define DEC_HEX_CONVERTER_KEY_FRAME_MARGIN 3
#define DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT 8
#define DEC_HEX_MAX_SUPORTED_DEC_INT 999999999
typedef enum {
EventTypeKey,
} EventType;
typedef struct {
InputEvent input;
EventType type;
} DecHexConverterEvent;
typedef enum {
ModeDec,
ModeHex,
} Mode;
// setting up one char array next to the other one causes the canvas_draw_str to display both of them
// when addressing the first one if there's no string terminator or similar indicator. Adding a \0 seems
// to work fine to prevent that, so add a final last char outside the size constants (added on init
// and NEVER changed nor referenced again)
//
// (as a reference, canvas_draw_str ends up calling u8g2_DrawStr from u8g2_font.c,
// that finally ends up calling u8g2_draw_string)
typedef struct {
char dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1];
char hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1];
Mode mode; // dec / hex
int8_t cursor; // position on keyboard (includes digit letters and other options)
int8_t digit_pos; // current digit on selected mode
} DecHexConverterState;
// move cursor left / right (TODO: implement menu nav in a more "standard" and reusable way?)
void dec_hex_converter_logic_move_cursor_lr(
DecHexConverterState* const dec_hex_converter_state,
int8_t d) {
dec_hex_converter_state->cursor += d;
if(dec_hex_converter_state->cursor > DEC_HEX_CONVERTER_KEYS - 1)
dec_hex_converter_state->cursor = 0;
else if(dec_hex_converter_state->cursor < 0)
dec_hex_converter_state->cursor = DEC_HEX_CONVERTER_KEYS - 1;
// if we're moving left / right to the letters keys on ModeDec just go to the closest available key
if(dec_hex_converter_state->mode == ModeDec) {
if(dec_hex_converter_state->cursor == 10)
dec_hex_converter_state->cursor = 16;
else if(dec_hex_converter_state->cursor == 15)
dec_hex_converter_state->cursor = 9;
}
}
// move cursor up / down; there're two lines, so we basically toggle
void dec_hex_converter_logic_move_cursor_ud(DecHexConverterState* const dec_hex_converter_state) {
if(dec_hex_converter_state->cursor < 9) {
// move to second line ("down")
dec_hex_converter_state->cursor += 9;
// if we're reaching the letter keys while ModeDec, just move left / right for the first available key
if(dec_hex_converter_state->mode == ModeDec &&
(dec_hex_converter_state->cursor >= 10 && dec_hex_converter_state->cursor <= 15)) {
if(dec_hex_converter_state->cursor <= 12)
dec_hex_converter_state->cursor = 9;
else
dec_hex_converter_state->cursor = 16;
}
} else {
// move to first line ("up")
dec_hex_converter_state->cursor -= 9;
}
}
// fetch number from current mode and modifies the destination one, RM dnt stel pls
// (if destination is shorter than the output value, overried with "-" chars or something similar)
void dec_hex_converter_logic_convert_number(DecHexConverterState* const dec_hex_converter_state) {
char* s_ptr;
char* d_ptr;
char dest[DEC_HEX_CONVERTER_NUMBER_DIGITS];
int i = 0; // current index on destination array
if(dec_hex_converter_state->mode == ModeDec) {
// DEC to HEX cannot overflow if they're, at least, the same size
s_ptr = dec_hex_converter_state->dec_number;
d_ptr = dec_hex_converter_state->hex_number;
int a = atoi(s_ptr);
int r;
while(a != 0) {
r = a % 16;
dest[i] = r + (r < 10 ? '0' : ('A' - 10));
a /= 16;
i++;
}
} else {
s_ptr = dec_hex_converter_state->hex_number;
d_ptr = dec_hex_converter_state->dec_number;
int a = strtol(s_ptr, NULL, 16);
if(a > DEC_HEX_MAX_SUPORTED_DEC_INT) {
// draw all "###" if there's an overflow
for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) {
d_ptr[j] = DEC_HEX_CONVERTER_CHAR_OVERFLOW;
}
return;
} else {
while(a > 0) {
dest[i++] = (a % 10) + '0';
a /= 10;
}
}
}
// dest is reversed, copy to destination pointer and append empty chars at the end
for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) {
if(i >= 1)
d_ptr[j] = dest[--i];
else
d_ptr[j] = ' ';
}
}
// change from DEC to HEX or HEX to DEC, set the digit_pos to the last position not empty on the destination mode
void dec_hex_converter_logic_swap_mode(DecHexConverterState* const dec_hex_converter_state) {
char* n_ptr;
if(dec_hex_converter_state->mode == ModeDec) {
dec_hex_converter_state->mode = ModeHex;
n_ptr = dec_hex_converter_state->hex_number;
} else {
dec_hex_converter_state->mode = ModeDec;
n_ptr = dec_hex_converter_state->dec_number;
}
dec_hex_converter_state->digit_pos = DEC_HEX_CONVERTER_NUMBER_DIGITS;
for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) {
if(n_ptr[i] == ' ') {
dec_hex_converter_state->digit_pos = i;
break;
}
}
}
// removes the number on current digit on current mode
static void
dec_hex_converter_logic_del_number(DecHexConverterState* const dec_hex_converter_state) {
if(dec_hex_converter_state->digit_pos > 0) dec_hex_converter_state->digit_pos--;
if(dec_hex_converter_state->mode == ModeDec) {
dec_hex_converter_state->dec_number[dec_hex_converter_state->digit_pos] = ' ';
} else {
dec_hex_converter_state->hex_number[dec_hex_converter_state->digit_pos] = ' ';
}
}
// append a number to the digit on the current mode
static void dec_hex_converter_logic_add_number(
DecHexConverterState* const dec_hex_converter_state,
int8_t number) {
// ignore HEX values on DEC mode (probably button nav will be disabled too, so cannot reach);
// also do not add anything if we're out of bound
if((number > 9 && dec_hex_converter_state->mode == ModeDec) ||
dec_hex_converter_state->digit_pos >= DEC_HEX_CONVERTER_NUMBER_DIGITS)
return;
char* s_ptr;
if(dec_hex_converter_state->mode == ModeDec) {
s_ptr = dec_hex_converter_state->dec_number;
} else {
s_ptr = dec_hex_converter_state->hex_number;
}
if(number < 10) {
s_ptr[dec_hex_converter_state->digit_pos] = number + '0';
} else {
s_ptr[dec_hex_converter_state->digit_pos] = (number - 10) + 'A'; // A-F (HEX only)
}
dec_hex_converter_state->digit_pos++;
}
// ---------------
static void dec_hex_converter_render_callback(Canvas* const canvas, void* ctx) {
const DecHexConverterState* dec_hex_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
if(dec_hex_converter_state == NULL) {
return;
}
canvas_set_color(canvas, ColorBlack);
// DEC
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, "DEC: ");
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10, dec_hex_converter_state->dec_number);
// HEX
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10 + 12, "HEX: ");
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10 + 12, dec_hex_converter_state->hex_number);
// current mode indicator
// char buffer[4];
// snprintf(buffer, sizeof(buffer), "%u", dec_hex_converter_state->digit_pos); // debug: show digit position instead of selected mode
if(dec_hex_converter_state->mode == ModeDec) {
canvas_draw_glyph(canvas, 128 - 10, 10, DEC_HEX_CONVERTER_CHAR_MODE);
} else {
canvas_draw_glyph(canvas, 128 - 10, 10 + 12, DEC_HEX_CONVERTER_CHAR_MODE);
}
// draw the line
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
// draw the keyboard
uint8_t _x = 5;
uint8_t _y = 25 + 15; // line + 10
for(int i = 0; i < DEC_HEX_CONVERTER_KEYS; i++) {
char g;
if(i < 10)
g = (i + '0');
else if(i < 16)
g = ((i - 10) + 'A');
else if(i == 16)
g = DEC_HEX_CONVERTER_CHAR_DEL; // '<'
else
g = DEC_HEX_CONVERTER_CHAR_SWAP; // 's'
uint8_t g_w = canvas_glyph_width(canvas, g);
// disable letters on DEC mode (but keep the previous width for visual purposes - show "blank keys")
if(dec_hex_converter_state->mode == ModeDec && i > 9 && i < 16) g = ' ';
if(dec_hex_converter_state->cursor == i) {
canvas_draw_box(
canvas,
_x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN,
_y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN),
DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN,
DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2);
canvas_set_color(canvas, ColorWhite);
canvas_draw_glyph(canvas, _x, _y, g);
canvas_set_color(canvas, ColorBlack);
} else {
canvas_draw_frame(
canvas,
_x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN,
_y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN),
DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN,
DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2);
canvas_draw_glyph(canvas, _x, _y, g);
}
if(i < 8) {
_x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 2;
} else if(i == 8) {
_y += (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2) + 3;
_x = 7; // some padding at the beginning on second line
} else {
_x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 1;
}
}
release_mutex((ValueMutex*)ctx, dec_hex_converter_state);
}
static void
dec_hex_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
DecHexConverterEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void dec_hex_converter_init(DecHexConverterState* const dec_hex_converter_state) {
dec_hex_converter_state->mode = ModeDec;
dec_hex_converter_state->digit_pos = 0;
dec_hex_converter_state->dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator
dec_hex_converter_state->hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator
for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) {
dec_hex_converter_state->dec_number[i] = ' ';
dec_hex_converter_state->hex_number[i] = ' ';
}
}
// main entry point
int32_t dec_hex_converter_app(void* p) {
UNUSED(p);
// get event queue
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(DecHexConverterEvent));
// allocate state
DecHexConverterState* dec_hex_converter_state = malloc(sizeof(DecHexConverterState));
// set mutex for plugin state (different threads can access it)
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, dec_hex_converter_state, sizeof(dec_hex_converter_state))) {
FURI_LOG_E("DecHexConverter", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(dec_hex_converter_state);
return 255;
}
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, dec_hex_converter_render_callback, &state_mutex);
view_port_input_callback_set(view_port, dec_hex_converter_input_callback, event_queue);
// open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
dec_hex_converter_init(dec_hex_converter_state);
// main loop
DecHexConverterEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
DecHexConverterState* dec_hex_converter_state =
(DecHexConverterState*)acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
default:
break;
case InputKeyUp:
case InputKeyDown:
dec_hex_converter_logic_move_cursor_ud(dec_hex_converter_state);
break;
case InputKeyRight:
dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, 1);
break;
case InputKeyLeft:
dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, -1);
break;
case InputKeyOk:
if(dec_hex_converter_state->cursor < DEC_HEX_CONVERTER_KEY_DEL) {
// positions from 0 to 15 works as regular numbers (DEC / HEX where applicable)
// (logic won't allow add numbers > 9 on ModeDec)
dec_hex_converter_logic_add_number(
dec_hex_converter_state, dec_hex_converter_state->cursor);
} else if(dec_hex_converter_state->cursor == DEC_HEX_CONVERTER_KEY_DEL) {
// del
dec_hex_converter_logic_del_number(dec_hex_converter_state);
} else {
// swap
dec_hex_converter_logic_swap_mode(dec_hex_converter_state);
}
dec_hex_converter_logic_convert_number(dec_hex_converter_state);
break;
case InputKeyBack:
processing = false;
break;
}
}
}
} else {
// event timeout
}
view_port_update(view_port);
release_mutex(&state_mutex, dec_hex_converter_state);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
free(dec_hex_converter_state);
return 0;
}

View File

@ -220,8 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager
furi_assert(blocking_animation);
animation_manager->sd_shown_sd_ok = true;
} else if(!animation_manager->sd_shown_no_db) {
bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK;
if(!db_exists) {
if(!storage_file_exists(storage, EXT_PATH("Manifest"))) {
blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME);
furi_assert(blocking_animation);
animation_manager->sd_shown_no_db = true;

View File

@ -94,6 +94,10 @@ bool slideshow_is_loaded(Slideshow* slideshow) {
return slideshow->loaded;
}
bool slideshow_is_one_page(Slideshow* slideshow) {
return slideshow->loaded && (slideshow->icon.frame_count == 1);
}
bool slideshow_advance(Slideshow* slideshow) {
uint8_t next_frame = slideshow->current_frame + 1;
if(next_frame < slideshow->icon.frame_count) {

View File

@ -9,6 +9,7 @@ Slideshow* slideshow_alloc();
void slideshow_free(Slideshow* slideshow);
bool slideshow_load(Slideshow* slideshow, const char* fspath);
bool slideshow_is_loaded(Slideshow* slideshow);
bool slideshow_is_one_page(Slideshow* slideshow);
void slideshow_goback(Slideshow* slideshow);
bool slideshow_advance(Slideshow* slideshow);
void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y);

View File

@ -23,11 +23,12 @@ void desktop_debug_render(Canvas* canvas, void* model) {
const Version* ver;
char buffer[64];
static const char* headers[] = {"FW Version Info:", "Dolphin Info:"};
static const char* headers[] = {"Device Info:", "Dolphin Info:"};
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 9 + STATUS_BAR_Y_SHIFT, headers[m->screen]);
canvas_draw_str_aligned(
canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]);
canvas_set_font(canvas, FontSecondary);
if(m->screen != DesktopViewStatsMeta) {
@ -44,7 +45,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
furi_hal_version_get_hw_region_name(),
furi_hal_region_get_name(),
my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer);
ver = furi_hal_version_get_firmware_version();
const BleGlueC2Info* c2_ver = NULL;
@ -52,7 +53,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
c2_ver = ble_glue_get_c2_info();
#endif
if(!ver) {
canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info");
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info");
return;
}
@ -62,7 +63,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
"%s [%s]",
version_get_version(ver),
version_get_builddate(ver));
canvas_draw_str(canvas, 5, 28 + STATUS_BAR_Y_SHIFT, buffer);
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(
buffer,
@ -72,11 +73,11 @@ void desktop_debug_render(Canvas* canvas, void* model) {
version_get_githash(ver),
version_get_gitbranchnum(ver),
c2_ver ? c2_ver->StackTypeString : "<none>");
canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 5, 50 + STATUS_BAR_Y_SHIFT, buffer);
canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);
} else {
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);

View File

@ -35,8 +35,9 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
furi_assert(event);
DesktopSlideshowView* instance = context;
DesktopSlideshowViewModel* model = view_get_model(instance->view);
bool update_view = false;
if(event->type == InputTypeShort) {
DesktopSlideshowViewModel* model = view_get_model(instance->view);
bool end_slideshow = false;
switch(event->key) {
case InputKeyLeft:
@ -54,15 +55,18 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
if(end_slideshow) {
instance->callback(DesktopSlideshowCompleted, instance->context);
}
view_commit_model(instance->view, true);
update_view = true;
} else if(event->key == InputKeyOk) {
if(event->type == InputTypePress) {
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT);
} else if(event->type == InputTypeRelease) {
furi_timer_stop(instance->timer);
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
/*if(!slideshow_is_one_page(model->slideshow)) {
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
}*/
}
}
view_commit_model(instance->view, update_view);
return true;
}
@ -79,12 +83,12 @@ static void desktop_view_slideshow_enter(void* context) {
instance->timer =
furi_timer_alloc(desktop_first_start_timer_callback, FuriTimerTypeOnce, instance);
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
DesktopSlideshowViewModel* model = view_get_model(instance->view);
model->slideshow = slideshow_alloc();
if(!slideshow_load(model->slideshow, SLIDESHOW_FS_PATH)) {
instance->callback(DesktopSlideshowCompleted, instance->context);
} else if(!slideshow_is_one_page(model->slideshow)) {
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
}
view_commit_model(instance->view, false);
}

View File

@ -14,8 +14,8 @@
#define DOLPHIN_STATE_PATH INT_PATH(DOLPHIN_STATE_FILE_NAME)
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
#define DOLPHIN_STATE_HEADER_VERSION 0x01
#define LEVEL2_THRESHOLD 735
#define LEVEL3_THRESHOLD 2940
#define LEVEL2_THRESHOLD 300
#define LEVEL3_THRESHOLD 1800
#define BUTTHURT_MAX 14
#define BUTTHURT_MIN 0

View File

@ -99,6 +99,11 @@ static bool browser_folder_check_and_switch(string_t path) {
FileInfo file_info;
Storage* storage = furi_record_open(RECORD_STORAGE);
bool is_root = false;
if(string_search_rchar(path, '/') == 0) {
is_root = true;
}
while(1) {
// Check if folder is existing and navigate back if not
if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) {

View File

@ -115,8 +115,8 @@ void widget_add_text_box_element(
* @param[in] text Formatted text. Default format: align left, Secondary font.
* The following formats are available:
* "\e#Bold text" - sets bold font before until next '\n' symbol
* "\ecBold text" - sets center horizontal align before until next '\n' symbol
* "\erBold text" - sets right horizontal align before until next '\n' symbol
* "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol
* "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol
*/
void widget_add_text_scroll_element(
Widget* widget,

View File

@ -1,9 +0,0 @@
App(
appid="hid_analyzer",
name="HID Analyzer",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_analyzer_app",
cdefines=["APP_HID_ANALYZER"],
stack_size=2 * 1024,
order=40,
)

View File

@ -1,98 +0,0 @@
#include "decoder_hid.h"
#include <furi_hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t jitter_time_us = 20;
constexpr uint32_t min_time_us = 64;
constexpr uint32_t max_time_us = 80;
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
bool DecoderHID::read(uint8_t* data, uint8_t data_size) {
bool result = false;
furi_assert(data_size >= 3);
if(ready) {
result = true;
hid.decode(
reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
ready = false;
}
return result;
}
void DecoderHID::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(polarity == true) {
last_pulse_time = time;
} else {
last_pulse_time += time;
if(last_pulse_time > min_time && last_pulse_time < max_time) {
bool pulse;
if(last_pulse_time < mid_time) {
// 6 pulses
pulse = false;
} else {
// 5 pulses
pulse = true;
}
if(last_pulse == pulse) {
pulse_count++;
if(pulse) {
if(pulse_count > 4) {
pulse_count = 0;
store_data(1);
}
} else {
if(pulse_count > 5) {
pulse_count = 0;
store_data(0);
}
}
} else {
if(last_pulse) {
if(pulse_count > 2) {
store_data(1);
}
} else {
if(pulse_count > 3) {
store_data(0);
}
}
pulse_count = 0;
last_pulse = pulse;
}
}
}
}
DecoderHID::DecoderHID() {
reset_state();
}
void DecoderHID::store_data(bool data) {
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
stored_data[2] = (stored_data[2] << 1) | data;
if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
ready = true;
}
}
void DecoderHID::reset_state() {
last_pulse = false;
pulse_count = 0;
ready = false;
last_pulse_time = 0;
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
#include "protocols/protocol_hid.h"
class DecoderHID {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderHID();
private:
uint32_t last_pulse_time = 0;
bool last_pulse;
uint8_t pulse_count;
uint32_t stored_data[3] = {0, 0, 0};
void store_data(bool data);
std::atomic<bool> ready;
void reset_state();
ProtocolHID hid;
};

View File

@ -1,143 +0,0 @@
#include "hid_reader.h"
#include <furi.h>
#include <furi_hal.h>
#include <stm32wbxx_ll_cortex.h>
/**
* @brief private violation assistant for HIDReader
*/
struct HIDReaderAccessor {
static void decode(HIDReader& hid_reader, bool polarity) {
hid_reader.decode(polarity);
}
};
void HIDReader::decode(bool polarity) {
uint32_t current_dwt_value = DWT->CYCCNT;
uint32_t period = current_dwt_value - last_dwt_value;
last_dwt_value = current_dwt_value;
decoder_hid.process_front(polarity, period);
detect_ticks++;
}
bool HIDReader::switch_timer_elapsed() {
const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f;
return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch;
}
void HIDReader::switch_timer_reset() {
switch_os_tick_last = furi_get_tick();
}
void HIDReader::switch_mode() {
switch(type) {
case Type::Normal:
type = Type::Indala;
furi_hal_rfid_change_read_config(62500.0f, 0.25f);
break;
case Type::Indala:
type = Type::Normal;
furi_hal_rfid_change_read_config(125000.0f, 0.5f);
break;
}
switch_timer_reset();
}
static void comparator_trigger_callback(bool level, void* comp_ctx) {
HIDReader* _this = static_cast<HIDReader*>(comp_ctx);
HIDReaderAccessor::decode(*_this, !level);
}
HIDReader::HIDReader() {
}
void HIDReader::start() {
type = Type::Normal;
furi_hal_rfid_pins_read();
furi_hal_rfid_tim_read(125000, 0.5);
furi_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_read_count = 0;
}
void HIDReader::start_forced(HIDReader::Type _type) {
start();
if(_type == Type::Indala) {
switch_mode();
}
}
void HIDReader::stop() {
furi_hal_rfid_pins_reset();
furi_hal_rfid_tim_read_stop();
furi_hal_rfid_tim_reset();
stop_comparator();
}
bool HIDReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
bool result = false;
bool something_read = false;
if(decoder_hid.read(data, data_size)) {
*_type = LfrfidKeyType::KeyH10301; // should be an OK temp
something_read = true;
}
// validation
if(something_read) {
switch_timer_reset();
if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
last_read_count = last_read_count + 1;
if(last_read_count > 2) {
result = true;
}
} else {
last_read_type = *_type;
memcpy(last_read_data, data, data_size);
last_read_count = 0;
}
}
// mode switching
if(switch_enable && switch_timer_elapsed()) {
switch_mode();
last_read_count = 0;
}
return result;
}
bool HIDReader::detect() {
bool detected = false;
if(detect_ticks > 10) {
detected = true;
}
detect_ticks = 0;
return detected;
}
bool HIDReader::any_read() {
return last_read_count > 0;
}
void HIDReader::start_comparator(void) {
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this);
last_dwt_value = DWT->CYCCNT;
furi_hal_rfid_comp_start();
}
void HIDReader::stop_comparator(void) {
furi_hal_rfid_comp_stop();
furi_hal_rfid_comp_set_callback(NULL, NULL);
}

View File

@ -1,47 +0,0 @@
#pragma once
#include "decoder_hid.h"
#include "key_info.h"
//#define RFID_GPIO_DEBUG 1
class HIDReader {
public:
enum class Type : uint8_t {
Normal,
Indala,
};
HIDReader();
void start();
void start_forced(HIDReader::Type type);
void stop();
bool read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable = true);
bool detect();
bool any_read();
private:
friend struct HIDReaderAccessor;
DecoderHID decoder_hid;
uint32_t last_dwt_value;
void start_comparator(void);
void stop_comparator(void);
void decode(bool polarity);
uint32_t detect_ticks;
uint32_t switch_os_tick_last;
bool switch_timer_elapsed();
void switch_timer_reset();
void switch_mode();
LfrfidKeyType last_read_type;
uint8_t last_read_data[LFRFID_KEY_SIZE];
uint8_t last_read_count;
Type type = Type::Normal;
};

View File

@ -1,38 +0,0 @@
#include "hid_worker.h"
HIDWorker::HIDWorker() {
}
HIDWorker::~HIDWorker() {
}
void HIDWorker::start_read() {
reader.start();
}
bool HIDWorker::read() {
static const uint8_t data_size = LFRFID_KEY_SIZE;
uint8_t data[data_size] = {0};
LfrfidKeyType type;
bool result = reader.read(&type, data, data_size);
if(result) {
key.set_type(type);
key.set_data(data, data_size);
};
return result;
}
bool HIDWorker::detect() {
return reader.detect();
}
bool HIDWorker::any_read() {
return reader.any_read();
}
void HIDWorker::stop_read() {
reader.stop();
}

View File

@ -1,21 +0,0 @@
#pragma once
#include "key_info.h"
#include "rfid_key.h"
#include "hid_reader.h"
class HIDWorker {
public:
HIDWorker();
~HIDWorker();
void start_read();
bool read();
bool detect();
bool any_read();
void stop_read();
RfidKey key;
private:
HIDReader reader;
};

View File

@ -1,16 +0,0 @@
#pragma once
#include <stdint.h>
static const uint8_t LFRFID_KEY_SIZE = 8;
static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
enum class LfrfidKeyType : uint8_t {
KeyEM4100,
KeyH10301,
KeyI40134,
};
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type);
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type);
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);

View File

@ -1,30 +0,0 @@
#pragma once
#include <stdint.h>
/**
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
*/
class OscFSK {
public:
/**
* Get next period
* @param bit bit value
* @param period return period
* @return bool whether to advance to the next bit
*/
bool next(bool bit, uint16_t* period);
/**
* FSK ocillator constructor
*
* @param freq_low bit 0 freq
* @param freq_hi bit 1 freq
* @param osc_phase_max max oscillator phase
*/
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
private:
const uint16_t freq[2];
const uint16_t osc_phase_max;
int32_t osc_phase_current;
};

View File

@ -1,60 +0,0 @@
#pragma once
#include "stdint.h"
#include "stdbool.h"
class ProtocolGeneric {
public:
/**
* @brief Get the encoded data size
*
* @return uint8_t size of encoded data in bytes
*/
virtual uint8_t get_encoded_data_size() = 0;
/**
* @brief Get the decoded data size
*
* @return uint8_t size of decoded data in bytes
*/
virtual uint8_t get_decoded_data_size() = 0;
/**
* @brief encode decoded data
*
* @param decoded_data
* @param decoded_data_size
* @param encoded_data
* @param encoded_data_size
*/
virtual void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) = 0;
/**
* @brief decode encoded data
*
* @param encoded_data
* @param encoded_data_size
* @param decoded_data
* @param decoded_data_size
*/
virtual void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) = 0;
/**
* @brief fast check that data can be correctly decoded
*
* @param encoded_data
* @param encoded_data_size
* @return true - can be correctly decoded
* @return false - cannot be correctly decoded
*/
virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
virtual ~ProtocolGeneric(){};
};

View File

@ -1,155 +0,0 @@
#include "protocol_hid.h"
#include <furi.h>
typedef uint32_t HIDCardData;
constexpr uint8_t HIDCount = 3;
constexpr uint8_t HIDBitSize = sizeof(HIDCardData) * 8;
uint8_t ProtocolHID::get_encoded_data_size() {
return sizeof(HIDCardData) * HIDCount;
}
uint8_t ProtocolHID::get_decoded_data_size() {
return 3;
}
void ProtocolHID::encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) {
UNUSED(decoded_data);
UNUSED(decoded_data_size);
UNUSED(encoded_data);
UNUSED(encoded_data_size);
// bob!
}
void ProtocolHID::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
// header check
int16_t second1pos = find_second_1(encoded_data);
if((*(encoded_data + 1) & 0b1100) != 0x08) {
*decoded_data = 37;
} else {
*decoded_data = (36 - (second1pos - 8));
}
}
int16_t ProtocolHID::find_second_1(const uint8_t* encoded_data) {
if((*(encoded_data + 1) & 0b11) == 0b10) {
return 8;
} else {
for(int8_t i = 3; i >= 0; i--) {
if(((*(encoded_data + 0) >> (2 * i)) & 0b11) == 0b10) {
return (12 - i);
}
}
for(int8_t i = 3; i >= 0; i--) {
if(((*(encoded_data + 7) >> (2 * i)) & 0b11) == 0b10) {
return (16 - i);
}
}
for(int8_t i = 3; i >= 2; i--) {
if(((*(encoded_data + 6) >> (2 * i)) & 0b11) == 0b10) {
return (20 - i);
}
}
}
return -1;
}
bool ProtocolHID::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
const HIDCardData* card_data = reinterpret_cast<const HIDCardData*>(encoded_data);
// header check
int16_t second1pos = -1;
// packet pre-preamble
if(*(encoded_data + 3) != 0x1D) {
return false;
}
// packet preamble
if(*(encoded_data + 2) != 0x55) { // first four 0s mandatory in preamble
return false;
}
if((*(encoded_data + 1) & 0xF0) != 0x50) { // next two 0s mandatory in preamble
return false;
}
if((*(encoded_data + 1) & 0b1100) != 0x08) { // if it's not a 1...
// either it's a 37-bit or invalid
// so just continue with the manchester encoding checks
} else { // it is a 1. so it could be anywhere between 26 and 36 bit encoding. or invalid.
// we need to find the location of the second 1
second1pos = find_second_1(encoded_data);
}
if(second1pos == -1) {
// we're 37 bit or invalid
}
// data decoding. ensure all is properly manchester encoded
uint32_t result = 0;
// decode from word 0
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 11; i >= 0; i--) {
switch((*(card_data + 0) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return false;
break;
}
}
// decode from word 1
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 15; i >= 0; i--) {
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return false;
break;
}
}
// decode from word 2
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 15; i >= 0; i--) {
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return false;
break;
}
}
return true;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "protocol_generic.h"
class ProtocolHID : public ProtocolGeneric {
public:
uint8_t get_encoded_data_size() final;
uint8_t get_decoded_data_size() final;
void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) final;
void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) final;
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
private:
int16_t find_second_1(const uint8_t* encoded_data);
};

View File

@ -1,36 +0,0 @@
#pragma once
#include "stdint.h"
class PulseJoiner {
public:
/**
* @brief Push timer pulse. First negative pulse is ommited.
*
* @param polarity pulse polarity: true = high2low, false = low2high
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*
* @return true - next pulse can and must be popped immediatly
*/
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
/**
* @brief Get the next timer pulse. Call only if push_pulse returns true.
*
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*/
void pop_pulse(uint16_t* period, uint16_t* pulse);
PulseJoiner();
private:
struct Pulse {
bool polarity;
uint16_t time;
};
uint8_t pulse_index = 0;
static const uint8_t pulse_max = 6;
Pulse pulses[pulse_max];
};

View File

@ -1,27 +0,0 @@
#pragma once
#include "key_info.h"
#include <array>
class RfidKey {
public:
RfidKey();
~RfidKey();
void set_type(LfrfidKeyType type);
void set_data(const uint8_t* data, const uint8_t data_size);
void set_name(const char* name);
LfrfidKeyType get_type();
const uint8_t* get_data();
const char* get_type_text();
uint8_t get_type_data_count() const;
char* get_name();
uint8_t get_name_length();
void clear();
RfidKey& operator=(const RfidKey& rhs);
private:
std::array<uint8_t, LFRFID_KEY_SIZE> data;
LfrfidKeyType type;
char name[LFRFID_KEY_NAME_SIZE + 1];
};

View File

@ -1,29 +0,0 @@
#pragma once
#include <furi_hal.h>
#include "key_info.h"
#include "encoder_generic.h"
#include "encoder_emmarin.h"
#include "encoder_hid_h10301.h"
#include "encoder_indala_40134.h"
#include "pulse_joiner.h"
#include <map>
class RfidTimerEmulator {
public:
RfidTimerEmulator();
~RfidTimerEmulator();
void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
void stop();
private:
EncoderGeneric* current_encoder = nullptr;
std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
{LfrfidKeyType::KeyEM4100, new EncoderEM()},
{LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
{LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
};
PulseJoiner pulse_joiner;
static void timer_update_callback(void* ctx);
};

View File

@ -1,20 +0,0 @@
#pragma once
#include "stdint.h"
class RfidWriter {
public:
RfidWriter();
~RfidWriter();
void start();
void stop();
void write_em(const uint8_t em_data[5]);
void write_hid(const uint8_t hid_data[3]);
void write_indala(const uint8_t indala_data[3]);
private:
void write_gap(uint32_t gap_time);
void write_bit(bool value);
void write_byte(uint8_t value);
void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
void write_reset();
};

View File

@ -1,25 +0,0 @@
#pragma once
#include "stdint.h"
#include <list>
#include <functional>
class TickSequencer {
public:
TickSequencer();
~TickSequencer();
void tick();
void reset();
void clear();
void do_every_tick(uint32_t tick_count, std::function<void(void)> fn);
void do_after_tick(uint32_t tick_count, std::function<void(void)> fn);
private:
std::list<std::pair<uint32_t, std::function<void(void)> > > list;
std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it;
uint32_t tick_count;
void do_nothing();
};

View File

@ -1,23 +0,0 @@
#include "hid_analyzer_app.h"
#include "scene/hid_analyzer_app_scene_read.h"
#include "scene/hid_analyzer_app_scene_read_success.h"
HIDApp::HIDApp()
: scene_controller{this}
, notification{"notification"}
, storage{"storage"}
, dialogs{"dialogs"}
, text_store(40) {
}
HIDApp::~HIDApp() {
}
void HIDApp::run(void* _args) {
UNUSED(_args);
view_controller.attach_to_gui(ViewDispatcherTypeFullscreen);
scene_controller.add_scene(SceneType::Read, new HIDAppSceneRead());
scene_controller.add_scene(SceneType::ReadSuccess, new HIDAppSceneReadSuccess());
scene_controller.process(100, SceneType::Read);
}

View File

@ -1,65 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <generic_scene.hpp>
#include <scene_controller.hpp>
#include <view_controller.hpp>
#include <record_controller.hpp>
#include <text_store.h>
#include <view_modules/submenu_vm.h>
#include <view_modules/popup_vm.h>
#include <view_modules/dialog_ex_vm.h>
#include <view_modules/text_input_vm.h>
#include <view_modules/byte_input_vm.h>
#include "view/container_vm.h"
#include <notification/notification_messages.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include "helpers/hid_worker.h"
class HIDApp {
public:
enum class EventType : uint8_t {
GENERIC_EVENT_ENUM_VALUES,
Next,
MenuSelected,
Stay,
Retry,
};
enum class SceneType : uint8_t {
GENERIC_SCENE_ENUM_VALUES,
Read,
ReadSuccess,
};
class Event {
public:
union {
int32_t menu_index;
} payload;
EventType type;
};
HIDApp();
~HIDApp();
void run(void* args);
// private:
SceneController<GenericScene<HIDApp>, HIDApp> scene_controller;
ViewController<HIDApp, SubmenuVM, PopupVM, DialogExVM, TextInputVM, ByteInputVM, ContainerVM>
view_controller;
RecordController<NotificationApp> notification;
RecordController<Storage> storage;
RecordController<DialogsApp> dialogs;
TextStore text_store;
HIDWorker worker;
};

View File

@ -1,10 +0,0 @@
#include "hid_analyzer_app.h"
// app enter function
extern "C" int32_t hid_analyzer_app(void* args) {
HIDApp* app = new HIDApp();
app->run(args);
delete app;
return 0;
}

View File

@ -1,40 +0,0 @@
#include "hid_analyzer_app_scene_read.h"
#include <dolphin/dolphin.h>
void HIDAppSceneRead::on_enter(HIDApp* app, bool /* need_restore */) {
auto popup = app->view_controller.get<PopupVM>();
DOLPHIN_DEED(DolphinDeedRfidRead);
popup->set_header("Searching for\nLF HID RFID", 89, 34, AlignCenter, AlignTop);
popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
app->view_controller.switch_to<PopupVM>();
app->worker.start_read();
}
bool HIDAppSceneRead::on_event(HIDApp* app, HIDApp::Event* event) {
bool consumed = false;
if(event->type == HIDApp::EventType::Tick) {
if(app->worker.read()) {
DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
notification_message(app->notification, &sequence_success);
app->scene_controller.switch_to_next_scene(HIDApp::SceneType::ReadSuccess);
} else {
if(app->worker.any_read()) {
notification_message(app->notification, &sequence_blink_green_10);
} else if(app->worker.detect()) {
notification_message(app->notification, &sequence_blink_cyan_10);
} else {
notification_message(app->notification, &sequence_blink_cyan_10);
}
}
}
return consumed;
}
void HIDAppSceneRead::on_exit(HIDApp* app) {
app->view_controller.get<PopupVM>()->clean();
app->worker.stop_read();
}

View File

@ -1,9 +0,0 @@
#pragma once
#include "../hid_analyzer_app.h"
class HIDAppSceneRead : public GenericScene<HIDApp> {
public:
void on_enter(HIDApp* app, bool need_restore) final;
bool on_event(HIDApp* app, HIDApp::Event* event) final;
void on_exit(HIDApp* app) final;
};

View File

@ -1,78 +0,0 @@
#include "hid_analyzer_app_scene_read_success.h"
#include "../view/elements/button_element.h"
#include "../view/elements/icon_element.h"
#include "../view/elements/string_element.h"
void HIDAppSceneReadSuccess::on_enter(HIDApp* app, bool /* need_restore */) {
string_init(string[0]);
string_init(string[1]);
string_init(string[2]);
auto container = app->view_controller.get<ContainerVM>();
auto button = container->add<ButtonElement>();
button->set_type(ButtonElement::Type::Left, "Retry");
button->set_callback(app, HIDAppSceneReadSuccess::back_callback);
auto icon = container->add<IconElement>();
icon->set_icon(3, 12, &I_RFIDBigChip_37x36);
auto header = container->add<StringElement>();
header->set_text("HID", 89, 3, 0, AlignCenter);
// auto line_1_text = container->add<StringElement>();
auto line_2_text = container->add<StringElement>();
// auto line_3_text = container->add<StringElement>();
// auto line_1_value = container->add<StringElement>();
auto line_2_value = container->add<StringElement>();
// auto line_3_value = container->add<StringElement>();
const uint8_t* data = app->worker.key.get_data();
// line_1_text->set_text("Hi:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
line_2_text->set_text("Bit:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
// line_3_text->set_text("Bye:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
string_printf(string[1], "%u", data[0]);
// line_1_value->set_text(
// string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
line_2_value->set_text(
string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
// line_3_value->set_text(
// string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
app->view_controller.switch_to<ContainerVM>();
notification_message_block(app->notification, &sequence_set_green_255);
}
bool HIDAppSceneReadSuccess::on_event(HIDApp* app, HIDApp::Event* event) {
bool consumed = false;
if(event->type == HIDApp::EventType::Retry) {
app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read});
consumed = true;
} else if(event->type == HIDApp::EventType::Back) {
app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read});
consumed = true;
}
return consumed;
}
void HIDAppSceneReadSuccess::on_exit(HIDApp* app) {
notification_message_block(app->notification, &sequence_reset_green);
app->view_controller.get<ContainerVM>()->clean();
string_clear(string[0]);
string_clear(string[1]);
string_clear(string[2]);
}
void HIDAppSceneReadSuccess::back_callback(void* context) {
HIDApp* app = static_cast<HIDApp*>(context);
HIDApp::Event event;
event.type = HIDApp::EventType::Retry;
app->view_controller.send_event(&event);
}

View File

@ -1,14 +0,0 @@
#pragma once
#include "../hid_analyzer_app.h"
class HIDAppSceneReadSuccess : public GenericScene<HIDApp> {
public:
void on_enter(HIDApp* app, bool need_restore) final;
bool on_event(HIDApp* app, HIDApp::Event* event) final;
void on_exit(HIDApp* app) final;
private:
static void back_callback(void* context);
string_t string[3];
};

View File

@ -1,17 +0,0 @@
#pragma once
#include <view_modules/generic_view_module.h>
class ContainerVM : public GenericViewModule {
public:
ContainerVM();
~ContainerVM() final;
View* get_view() final;
void clean() final;
template <typename T> T* add();
private:
View* view;
static void view_draw_callback(Canvas* canvas, void* model);
static bool view_input_callback(InputEvent* event, void* context);
};

View File

@ -1,28 +0,0 @@
#pragma once
#include "generic_element.h"
typedef void (*ButtonElementCallback)(void* context);
class ButtonElement : public GenericElement {
public:
ButtonElement();
~ButtonElement() final;
void draw(Canvas* canvas) final;
bool input(InputEvent* event) final;
enum class Type : uint8_t {
Left,
Center,
Right,
};
void set_type(Type type, const char* text);
void set_callback(void* context, ButtonElementCallback callback);
private:
Type type = Type::Left;
const char* text = nullptr;
void* context = nullptr;
ButtonElementCallback callback = nullptr;
};

View File

@ -1,21 +0,0 @@
#pragma once
#include <gui/gui.h>
#include <gui/view.h>
class GenericElement {
public:
GenericElement(){};
virtual ~GenericElement(){};
virtual void draw(Canvas* canvas) = 0;
virtual bool input(InputEvent* event) = 0;
// TODO that must be accessible only to ContainerVMData
void set_parent_view(View* view);
// TODO that must be accessible only to inheritors
void lock_model();
void unlock_model(bool need_redraw);
private:
View* view = nullptr;
};

View File

@ -1,17 +0,0 @@
#pragma once
#include "generic_element.h"
class IconElement : public GenericElement {
public:
IconElement();
~IconElement() final;
void draw(Canvas* canvas) final;
bool input(InputEvent* event) final;
void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL);
private:
const Icon* icon = NULL;
uint8_t x = 0;
uint8_t y = 0;
};

View File

@ -1,28 +0,0 @@
#pragma once
#include "generic_element.h"
class StringElement : public GenericElement {
public:
StringElement();
~StringElement() final;
void draw(Canvas* canvas) final;
bool input(InputEvent* event) final;
void set_text(
const char* text = NULL,
uint8_t x = 0,
uint8_t y = 0,
uint8_t fit_width = 0,
Align horizontal = AlignLeft,
Align vertical = AlignTop,
Font font = FontPrimary);
private:
const char* text = NULL;
uint8_t x = 0;
uint8_t y = 0;
uint8_t fit_width = 0;
Align horizontal = AlignLeft;
Align vertical = AlignTop;
Font font = FontPrimary;
};

View File

@ -17,6 +17,7 @@ ADD_SCENE(infrared, universal, Universal)
ADD_SCENE(infrared, universal_tv, UniversalTV)
ADD_SCENE(infrared, universal_ac, UniversalAC)
ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)
ADD_SCENE(infrared, rpc, Rpc)

View File

@ -3,6 +3,7 @@
typedef enum {
SubmenuIndexUniversalTV,
SubmenuIndexUniversalAudio,
SubmenuIndexUniversalProjector,
SubmenuIndexUniversalAirConditioner,
} SubmenuIndex;
@ -30,6 +31,13 @@ void infrared_scene_universal_on_enter(void* context) {
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"Projectors",
SubmenuIndexUniversalProjector,
infrared_scene_universal_submenu_callback,
context);
submenu_add_item(
submenu,
"ACs",
@ -52,6 +60,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexUniversalAudio) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio);
consumed = true;
} else if(event.event == SubmenuIndexUniversalProjector) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);
consumed = true;
} else if(event.event == SubmenuIndexUniversalAirConditioner) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
consumed = true;

View File

@ -0,0 +1,86 @@
#include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_projector_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context);
Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force;
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projectors.ir"));
//TODO Improve Projectors universal remote
button_panel_reserve(button_panel, 2, 2);
uint32_t i = 0;
button_panel_add_item(
button_panel,
i,
0,
0,
3,
19,
&I_Power_25x27,
&I_Power_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "POWER");
button_panel_add_item(
button_panel,
i,
1,
0,
36,
19,
&I_Mute_25x27,
&I_Mute_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "MUTE");
button_panel_add_item(
button_panel,
i,
0,
1,
3,
64,
&I_Vol_up_25x27,
&I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "VOL+");
button_panel_add_item(
button_panel,
i,
1,
1,
36,
64,
&I_Vol_down_25x27,
&I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback,
context);
infrared_brute_force_add_record(brute_force, i++, "VOL-");
button_panel_add_label(button_panel, 10, 11, FontPrimary, "Projector");
button_panel_add_label(button_panel, 17, 60, FontSecondary, "Volume");
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
infrared_show_loading_popup(infrared, true);
bool success = infrared_brute_force_calculate_messages(brute_force);
infrared_show_loading_popup(infrared, false);
if(!success) {
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
}
}
bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) {
return infrared_scene_universal_common_on_event(context, event);
}
void infrared_scene_universal_projector_on_exit(void* context) {
infrared_scene_universal_common_on_exit(context);
}

View File

@ -1,50 +0,0 @@
#include "decoder_analyzer.h"
#include <furi.h>
#include <furi_hal.h>
// FIXME: unused args?
bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) {
bool result = false;
if(ready) {
result = true;
for(size_t i = 0; i < data_size; i++) {
printf("%lu ", data[i]);
if((i + 1) % 8 == 0) printf("\r\n");
}
printf("\r\n--------\r\n");
ready = false;
}
return result;
}
void DecoderAnalyzer::process_front(bool polarity, uint32_t time) {
UNUSED(polarity);
if(ready) return;
data[data_index] = time;
if(data_index < data_size) {
data_index++;
} else {
data_index = 0;
ready = true;
}
}
DecoderAnalyzer::DecoderAnalyzer() {
data = reinterpret_cast<uint32_t*>(calloc(data_size, sizeof(uint32_t)));
furi_check(data);
data_index = 0;
ready = false;
}
DecoderAnalyzer::~DecoderAnalyzer() {
free(data);
}
void DecoderAnalyzer::reset_state() {
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderAnalyzer {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderAnalyzer();
~DecoderAnalyzer();
private:
void reset_state();
std::atomic<bool> ready;
static const uint32_t data_size = 2048;
uint32_t data_index = 0;
uint32_t* data;
};

View File

@ -1,72 +0,0 @@
#include "emmarin.h"
#include "decoder_emmarin.h"
#include <furi.h>
#include <furi_hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t short_time = 255 * clocks_in_us;
constexpr uint32_t long_time = 510 * clocks_in_us;
constexpr uint32_t jitter_time = 100 * clocks_in_us;
constexpr uint32_t short_time_low = short_time - jitter_time;
constexpr uint32_t short_time_high = short_time + jitter_time;
constexpr uint32_t long_time_low = long_time - jitter_time;
constexpr uint32_t long_time_high = long_time + jitter_time;
void DecoderEMMarin::reset_state() {
ready = false;
read_data = 0;
manchester_advance(
manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
}
bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) {
bool result = false;
if(ready) {
result = true;
em_marin.decode(
reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t), data, data_size);
ready = false;
}
return result;
}
void DecoderEMMarin::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(time < short_time_low) return;
ManchesterEvent event = ManchesterEventReset;
if(time > short_time_low && time < short_time_high) {
if(polarity) {
event = ManchesterEventShortHigh;
} else {
event = ManchesterEventShortLow;
}
} else if(time > long_time_low && time < long_time_high) {
if(polarity) {
event = ManchesterEventLongHigh;
} else {
event = ManchesterEventLongLow;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok =
manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
if(data_ok) {
read_data = (read_data << 1) | data;
ready = em_marin.can_be_decoded(
reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t));
}
}
}
DecoderEMMarin::DecoderEMMarin() {
reset_state();
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
#include <lib/toolbox/manchester_decoder.h>
#include "protocols/protocol_emmarin.h"
class DecoderEMMarin {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderEMMarin();
private:
void reset_state();
uint64_t read_data = 0;
std::atomic<bool> ready;
ManchesterState manchester_saved_state;
ProtocolEMMarin em_marin;
};

View File

@ -1,15 +0,0 @@
#include "decoder_gpio_out.h"
#include <furi.h>
#include <furi_hal.h>
void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) {
furi_hal_gpio_write(&gpio_ext_pa7, polarity);
}
DecoderGpioOut::DecoderGpioOut() {
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
}
DecoderGpioOut::~DecoderGpioOut() {
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
}

View File

@ -1,14 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderGpioOut {
public:
void process_front(bool polarity, uint32_t time);
DecoderGpioOut();
~DecoderGpioOut();
private:
void reset_state();
};

View File

@ -1,98 +0,0 @@
#include "decoder_hid26.h"
#include <furi_hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t jitter_time_us = 20;
constexpr uint32_t min_time_us = 64;
constexpr uint32_t max_time_us = 80;
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
bool result = false;
furi_assert(data_size >= 3);
if(ready) {
result = true;
hid.decode(
reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
ready = false;
}
return result;
}
void DecoderHID26::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(polarity == true) {
last_pulse_time = time;
} else {
last_pulse_time += time;
if(last_pulse_time > min_time && last_pulse_time < max_time) {
bool pulse;
if(last_pulse_time < mid_time) {
// 6 pulses
pulse = false;
} else {
// 5 pulses
pulse = true;
}
if(last_pulse == pulse) {
pulse_count++;
if(pulse) {
if(pulse_count > 4) {
pulse_count = 0;
store_data(1);
}
} else {
if(pulse_count > 5) {
pulse_count = 0;
store_data(0);
}
}
} else {
if(last_pulse) {
if(pulse_count > 2) {
store_data(1);
}
} else {
if(pulse_count > 3) {
store_data(0);
}
}
pulse_count = 0;
last_pulse = pulse;
}
}
}
}
DecoderHID26::DecoderHID26() {
reset_state();
}
void DecoderHID26::store_data(bool data) {
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
stored_data[2] = (stored_data[2] << 1) | data;
if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
ready = true;
}
}
void DecoderHID26::reset_state() {
last_pulse = false;
pulse_count = 0;
ready = false;
last_pulse_time = 0;
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
#include "protocols/protocol_hid_h10301.h"
class DecoderHID26 {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderHID26();
private:
uint32_t last_pulse_time = 0;
bool last_pulse;
uint8_t pulse_count;
uint32_t stored_data[3] = {0, 0, 0};
void store_data(bool data);
std::atomic<bool> ready;
void reset_state();
ProtocolHID10301 hid;
};

View File

@ -1,76 +0,0 @@
#include "decoder_indala.h"
#include <furi_hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t us_per_bit = 255;
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
bool result = false;
if(ready) {
result = true;
if(cursed_data_valid) {
indala.decode(
reinterpret_cast<const uint8_t*>(&cursed_raw_data),
sizeof(uint64_t),
data,
data_size);
} else {
indala.decode(
reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
}
reset_state();
}
return result;
}
void DecoderIndala::process_front(bool polarity, uint32_t time) {
if(ready) return;
process_internal(polarity, time, &raw_data);
if(ready) return;
if(polarity) {
time = time + 110;
} else {
time = time - 110;
}
process_internal(!polarity, time, &cursed_raw_data);
if(ready) {
cursed_data_valid = true;
}
}
void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
time /= clocks_in_us;
time += (us_per_bit / 2);
uint32_t bit_count = (time / us_per_bit);
if(bit_count < 64) {
for(uint32_t i = 0; i < bit_count; i++) {
*data = (*data << 1) | polarity;
if((*data >> 32) == 0xa0000000ULL) {
if(indala.can_be_decoded(
reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
ready = true;
break;
}
}
}
}
}
DecoderIndala::DecoderIndala() {
reset_state();
}
void DecoderIndala::reset_state() {
raw_data = 0;
cursed_raw_data = 0;
ready = false;
cursed_data_valid = false;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <stdint.h>
#include <limits.h>
#include <atomic>
#include "protocols/protocol_indala_40134.h"
class DecoderIndala {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
void process_internal(bool polarity, uint32_t time, uint64_t* data);
DecoderIndala();
private:
void reset_state();
uint64_t raw_data;
uint64_t cursed_raw_data;
std::atomic<bool> ready;
std::atomic<bool> cursed_data_valid;
ProtocolIndala40134 indala;
};

View File

@ -1,107 +0,0 @@
#include "decoder_ioprox.h"
#include <furi_hal.h>
#include <cli/cli.h>
#include <utility>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t jitter_time_us = 20;
constexpr uint32_t min_time_us = 64;
constexpr uint32_t max_time_us = 80;
constexpr uint32_t baud_time_us = 500;
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
constexpr uint32_t baud_time = baud_time_us * clocks_in_us;
bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) {
bool result = false;
furi_assert(data_size >= 4);
if(ready) {
result = true;
ioprox.decode(raw_data, sizeof(raw_data), data, data_size);
ready = false;
}
return result;
}
void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) {
if(ready) {
return;
}
// Always track the time that's gone by.
current_period_duration += time;
demodulation_sample_duration += time;
// If a baud time has elapsed, we're at a sample point.
if(demodulation_sample_duration >= baud_time) {
// Start a new baud period...
demodulation_sample_duration = 0;
demodulated_value_invalid = false;
// ... and if we didn't have any baud errors, capture a sample.
if(!demodulated_value_invalid) {
store_data(current_demodulated_value);
}
}
//
// FSK demodulator.
//
// If this isn't a rising edge, this isn't a pulse of interest.
// We're done.
if(!is_rising_edge) {
return;
}
bool is_valid_low = (current_period_duration > min_time) &&
(current_period_duration <= mid_time);
bool is_valid_high = (current_period_duration > mid_time) &&
(current_period_duration < max_time);
// If this is between the minimum and our threshold, this is a logical 0.
if(is_valid_low) {
current_demodulated_value = false;
}
// Otherwise, if between our threshold and the max time, it's a logical 1.
else if(is_valid_high) {
current_demodulated_value = true;
}
// Otherwise, invalidate this sample.
else {
demodulated_value_invalid = true;
}
// We're starting a new period; track that.
current_period_duration = 0;
}
DecoderIoProx::DecoderIoProx() {
reset_state();
}
void DecoderIoProx::store_data(bool data) {
for(int i = 0; i < 7; ++i) {
raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1);
}
raw_data[7] = (raw_data[7] << 1) | data;
if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) {
ready = true;
}
}
void DecoderIoProx::reset_state() {
current_demodulated_value = false;
demodulated_value_invalid = false;
current_period_duration = 0;
demodulation_sample_duration = 0;
ready = false;
}

View File

@ -1,26 +0,0 @@
#pragma once
#include <stdint.h>
#include <atomic>
#include "protocols/protocol_ioprox.h"
class DecoderIoProx {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderIoProx();
private:
uint32_t current_period_duration = 0;
uint32_t demodulation_sample_duration = 0;
bool current_demodulated_value = false;
bool demodulated_value_invalid = false;
uint8_t raw_data[8] = {0};
void store_data(bool data);
std::atomic<bool> ready;
void reset_state();
ProtocolIoProx ioprox;
};

View File

@ -1,15 +0,0 @@
#pragma once
#include <stdint.h>
#define EM_HEADER_POS 55
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
#define EM_FIRST_ROW_POS 50
#define EM_ROW_COUNT 10
#define EM_COLUMN_POS 4
#define EM_STOP_POS 0
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)

View File

@ -1,24 +0,0 @@
#include "encoder_emmarin.h"
#include "protocols/protocol_emmarin.h"
#include <furi.h>
void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
ProtocolEMMarin em_marin;
em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(uint64_t));
card_data_index = 0;
}
// data transmitted as manchester encoding
// 0 - high2low
// 1 - low2high
void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
*period = clocks_per_bit;
*pulse = clocks_per_bit / 2;
*polarity = (card_data >> (63 - card_data_index)) & 1;
card_data_index++;
if(card_data_index >= 64) {
card_data_index = 0;
}
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "encoder_generic.h"
class EncoderEM : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data 1 byte FC, next 4 byte SN
* @param data_size must be 5
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
private:
// clock pulses per bit
static const uint8_t clocks_per_bit = 64;
uint64_t card_data;
uint8_t card_data_index;
};

View File

@ -1,27 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
class EncoderGeneric {
public:
/**
* @brief init encoder
*
* @param data data array
* @param data_size data array size
*/
virtual void init(const uint8_t* data, const uint8_t data_size) = 0;
/**
* @brief Get the next timer pulse
*
* @param polarity pulse polarity true = high2low, false = low2high
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*/
virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0;
virtual ~EncoderGeneric(){};
private:
};

View File

@ -1,46 +0,0 @@
#include "encoder_hid_h10301.h"
#include "protocols/protocol_hid_h10301.h"
#include <furi.h>
void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
ProtocolHID10301 hid;
hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
card_data_index = 0;
}
void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
write_raw_bit(bit, position + 0);
write_raw_bit(!bit, position + 1);
}
void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
if(bit) {
card_data[position / 32] |= 1UL << (31 - (position % 32));
} else {
card_data[position / 32] &= ~(1UL << (31 - (position % 32)));
}
}
void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
bool advance = fsk->next(bit, period);
if(advance) {
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
}
*polarity = true;
*pulse = *period / 2;
}
EncoderHID_H10301::EncoderHID_H10301() {
fsk = new OscFSK(8, 10, 50);
}
EncoderHID_H10301::~EncoderHID_H10301() {
delete fsk;
}

View File

@ -1,26 +0,0 @@
#pragma once
#include "encoder_generic.h"
#include "osc_fsk.h"
class EncoderHID_H10301 : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data 1 byte FC, next 2 byte SN
* @param data_size must be 3
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
EncoderHID_H10301();
~EncoderHID_H10301();
private:
static const uint8_t card_data_max = 3;
uint32_t card_data[card_data_max];
uint8_t card_data_index;
void write_bit(bool bit, uint8_t position);
void write_raw_bit(bool bit, uint8_t position);
OscFSK* fsk;
};

View File

@ -1,36 +0,0 @@
#include "encoder_indala_40134.h"
#include "protocols/protocol_indala_40134.h"
#include <furi.h>
void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) {
ProtocolIndala40134 indala;
indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data));
last_bit = card_data & 1;
card_data_index = 0;
current_polarity = true;
}
void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
*period = 2;
*pulse = 1;
*polarity = current_polarity;
bit_clock_index++;
if(bit_clock_index >= clock_per_bit) {
bit_clock_index = 0;
bool current_bit = (card_data >> (63 - card_data_index)) & 1;
if(current_bit != last_bit) {
current_polarity = !current_polarity;
}
last_bit = current_bit;
card_data_index++;
if(card_data_index >= 64) {
card_data_index = 0;
}
}
}

View File

@ -1,23 +0,0 @@
#pragma once
#include "encoder_generic.h"
class EncoderIndala_40134 : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data indala raw data
* @param data_size must be 5
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
private:
uint64_t card_data;
uint8_t card_data_index;
uint8_t bit_clock_index;
bool last_bit;
bool current_polarity;
static const uint8_t clock_per_bit = 16;
};

View File

@ -1,32 +0,0 @@
#include "encoder_ioprox.h"
#include "protocols/protocol_ioprox.h"
#include <furi.h>
void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) {
ProtocolIoProx ioprox;
ioprox.encode(data, data_size, card_data, sizeof(card_data));
card_data_index = 0;
}
void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1;
bool advance = fsk->next(bit, period);
if(advance) {
card_data_index++;
if(card_data_index >= (8 * card_data_max)) {
card_data_index = 0;
}
}
*polarity = true;
*pulse = *period / 2;
}
EncoderIoProx::EncoderIoProx() {
fsk = new OscFSK(8, 10, 64);
}
EncoderIoProx::~EncoderIoProx() {
delete fsk;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "encoder_generic.h"
#include "osc_fsk.h"
class EncoderIoProx : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data 1 byte FC, 1 byte Version, 2 bytes code
* @param data_size must be 4
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
EncoderIoProx();
~EncoderIoProx();
private:
static const uint8_t card_data_max = 8;
uint8_t card_data[card_data_max];
uint8_t card_data_index;
OscFSK* fsk;
};

View File

@ -1,76 +0,0 @@
#include "key_info.h"
#include <string.h>
const char* lfrfid_key_get_type_string(LfrfidKeyType type) {
switch(type) {
case LfrfidKeyType::KeyEM4100:
return "EM4100";
break;
case LfrfidKeyType::KeyH10301:
return "H10301";
break;
case LfrfidKeyType::KeyI40134:
return "I40134";
break;
case LfrfidKeyType::KeyIoProxXSF:
return "IoProxXSF";
break;
}
return "Unknown";
}
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) {
switch(type) {
case LfrfidKeyType::KeyEM4100:
return "EM-Marin";
break;
case LfrfidKeyType::KeyH10301:
return "HID";
break;
case LfrfidKeyType::KeyI40134:
return "Indala";
break;
case LfrfidKeyType::KeyIoProxXSF:
return "Kantech";
}
return "Unknown";
}
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) {
bool result = true;
if(strcmp("EM4100", string) == 0) {
*type = LfrfidKeyType::KeyEM4100;
} else if(strcmp("H10301", string) == 0) {
*type = LfrfidKeyType::KeyH10301;
} else if(strcmp("I40134", string) == 0) {
*type = LfrfidKeyType::KeyI40134;
} else if(strcmp("IoProxXSF", string) == 0) {
*type = LfrfidKeyType::KeyIoProxXSF;
} else {
result = false;
}
return result;
}
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) {
switch(type) {
case LfrfidKeyType::KeyEM4100:
return 5;
break;
case LfrfidKeyType::KeyH10301:
return 3;
break;
case LfrfidKeyType::KeyI40134:
return 3;
break;
case LfrfidKeyType::KeyIoProxXSF:
return 4;
break;
}
return 0;
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <stdint.h>
static const uint8_t LFRFID_KEY_SIZE = 8;
static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
enum class LfrfidKeyType : uint8_t {
KeyEM4100,
KeyH10301,
KeyI40134,
KeyIoProxXSF,
};
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type);
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type);
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);

View File

@ -1,20 +0,0 @@
#include "osc_fsk.h"
OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max)
: freq{_freq_low, _freq_hi}
, osc_phase_max(_osc_phase_max) {
osc_phase_current = 0;
}
bool OscFSK::next(bool bit, uint16_t* period) {
bool advance = false;
*period = freq[bit];
osc_phase_current += *period;
if(osc_phase_current > osc_phase_max) {
advance = true;
osc_phase_current -= osc_phase_max;
}
return advance;
}

View File

@ -1,30 +0,0 @@
#pragma once
#include <stdint.h>
/**
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
*/
class OscFSK {
public:
/**
* Get next period
* @param bit bit value
* @param period return period
* @return bool whether to advance to the next bit
*/
bool next(bool bit, uint16_t* period);
/**
* FSK ocillator constructor
*
* @param freq_low bit 0 freq
* @param freq_hi bit 1 freq
* @param osc_phase_max max oscillator phase
*/
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
private:
const uint16_t freq[2];
const uint16_t osc_phase_max;
int32_t osc_phase_current;
};

View File

@ -1,150 +0,0 @@
#include "protocol_emmarin.h"
#include <furi.h>
#define EM_HEADER_POS 55
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
#define EM_FIRST_ROW_POS 50
#define EM_ROW_COUNT 10
#define EM_COLUMN_COUNT 4
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
#define EM_COLUMN_POS 4
#define EM_STOP_POS 0
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
typedef uint64_t EMMarinCardData;
void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) {
uint8_t parity_sum = 0;
uint8_t start = 0;
if(!low_nibble) start = 4;
for(int8_t i = (start + 3); i >= start; i--) {
parity_sum += (data >> i) & 1;
*card_data = (*card_data << 1) | ((data >> i) & 1);
}
*card_data = (*card_data << 1) | ((parity_sum % 2) & 1);
}
uint8_t ProtocolEMMarin::get_encoded_data_size() {
return sizeof(EMMarinCardData);
}
uint8_t ProtocolEMMarin::get_decoded_data_size() {
return 5;
}
void ProtocolEMMarin::encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
EMMarinCardData card_data;
// header
card_data = 0b111111111;
// data
for(uint8_t i = 0; i < get_decoded_data_size(); i++) {
write_nibble(false, decoded_data[i], &card_data);
write_nibble(true, decoded_data[i], &card_data);
}
// column parity and stop bit
uint8_t parity_sum;
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
parity_sum = 0;
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
parity_sum += parity_bit;
}
card_data = (card_data << 1) | ((parity_sum % 2) & 1);
}
// stop bit
card_data = (card_data << 1) | 0;
memcpy(encoded_data, &card_data, get_encoded_data_size());
}
void ProtocolEMMarin::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
uint8_t decoded_data_index = 0;
EMMarinCardData card_data = *(reinterpret_cast<const EMMarinCardData*>(encoded_data));
// clean result
memset(decoded_data, 0, decoded_data_size);
// header
for(uint8_t i = 0; i < 9; i++) {
card_data = card_data << 1;
}
// nibbles
uint8_t value = 0;
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
uint8_t nibble = 0;
for(uint8_t i = 0; i < 5; i++) {
if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
card_data = card_data << 1;
}
value = (value << 4) | nibble;
if(r % 2) {
decoded_data[decoded_data_index] |= value;
decoded_data_index++;
value = 0;
}
}
}
bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
const EMMarinCardData* card_data = reinterpret_cast<const EMMarinCardData*>(encoded_data);
// check header and stop bit
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
// check row parity
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
}
if((parity_sum % 2)) {
return false;
}
}
// check columns parity
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
}
if((parity_sum % 2)) {
return false;
}
}
return true;
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "protocol_generic.h"
class ProtocolEMMarin : public ProtocolGeneric {
public:
uint8_t get_encoded_data_size() final;
uint8_t get_decoded_data_size() final;
void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) final;
void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) final;
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
};

View File

@ -1,60 +0,0 @@
#pragma once
#include "stdint.h"
#include "stdbool.h"
class ProtocolGeneric {
public:
/**
* @brief Get the encoded data size
*
* @return uint8_t size of encoded data in bytes
*/
virtual uint8_t get_encoded_data_size() = 0;
/**
* @brief Get the decoded data size
*
* @return uint8_t size of decoded data in bytes
*/
virtual uint8_t get_decoded_data_size() = 0;
/**
* @brief encode decoded data
*
* @param decoded_data
* @param decoded_data_size
* @param encoded_data
* @param encoded_data_size
*/
virtual void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) = 0;
/**
* @brief decode encoded data
*
* @param encoded_data
* @param encoded_data_size
* @param decoded_data
* @param decoded_data_size
*/
virtual void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) = 0;
/**
* @brief fast check that data can be correctly decoded
*
* @param encoded_data
* @param encoded_data_size
* @return true - can be correctly decoded
* @return false - cannot be correctly decoded
*/
virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
virtual ~ProtocolGeneric(){};
};

View File

@ -1,238 +0,0 @@
#include "protocol_hid_h10301.h"
#include <furi.h>
typedef uint32_t HID10301CardData;
constexpr uint8_t HID10301Count = 3;
constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8;
static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
if(bit) {
card_data[position / HID10301BitSize] |=
1UL << (HID10301BitSize - (position % HID10301BitSize) - 1);
} else {
card_data[position / (sizeof(HID10301CardData) * 8)] &=
~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1));
}
}
static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
write_raw_bit(bit, position + 0, card_data);
write_raw_bit(!bit, position + 1, card_data);
}
uint8_t ProtocolHID10301::get_encoded_data_size() {
return sizeof(HID10301CardData) * HID10301Count;
}
uint8_t ProtocolHID10301::get_decoded_data_size() {
return 3;
}
void ProtocolHID10301::encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
HID10301CardData card_data[HID10301Count] = {0, 0, 0};
uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
// even parity sum calculation (high 12 bits of data)
uint8_t even_parity_sum = 0;
for(int8_t i = 12; i < 24; i++) {
if(((fc_cn >> i) & 1) == 1) {
even_parity_sum++;
}
}
// odd parity sum calculation (low 12 bits of data)
uint8_t odd_parity_sum = 1;
for(int8_t i = 0; i < 12; i++) {
if(((fc_cn >> i) & 1) == 1) {
odd_parity_sum++;
}
}
// 0x1D preamble
write_raw_bit(0, 0, card_data);
write_raw_bit(0, 1, card_data);
write_raw_bit(0, 2, card_data);
write_raw_bit(1, 3, card_data);
write_raw_bit(1, 4, card_data);
write_raw_bit(1, 5, card_data);
write_raw_bit(0, 6, card_data);
write_raw_bit(1, 7, card_data);
// company / OEM code 1
write_bit(0, 8, card_data);
write_bit(0, 10, card_data);
write_bit(0, 12, card_data);
write_bit(0, 14, card_data);
write_bit(0, 16, card_data);
write_bit(0, 18, card_data);
write_bit(1, 20, card_data);
// card format / length 1
write_bit(0, 22, card_data);
write_bit(0, 24, card_data);
write_bit(0, 26, card_data);
write_bit(0, 28, card_data);
write_bit(0, 30, card_data);
write_bit(0, 32, card_data);
write_bit(0, 34, card_data);
write_bit(0, 36, card_data);
write_bit(0, 38, card_data);
write_bit(0, 40, card_data);
write_bit(1, 42, card_data);
// even parity bit
write_bit((even_parity_sum % 2), 44, card_data);
// data
for(uint8_t i = 0; i < 24; i++) {
write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
}
// odd parity bit
write_bit((odd_parity_sum % 2), 94, card_data);
memcpy(encoded_data, &card_data, get_encoded_data_size());
}
void ProtocolHID10301::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
// data decoding
uint32_t result = 0;
// decode from word 1
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 9; i >= 0; i--) {
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
break;
}
}
// decode from word 2
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 15; i >= 0; i--) {
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
break;
}
}
uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
memcpy(decoded_data, &data, get_decoded_data_size());
}
bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
// packet preamble
// raw data
if(*(encoded_data + 3) != 0x1D) {
return false;
}
// encoded company/oem
// coded with 01 = 0, 10 = 1 transitions
// stored in word 0
if((*card_data >> 10 & 0x3FFF) != 0x1556) {
return false;
}
// encoded format/length
// coded with 01 = 0, 10 = 1 transitions
// stored in word 0 and word 1
if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
return false;
}
// data decoding
uint32_t result = 0;
// decode from word 1
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 9; i >= 0; i--) {
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return false;
break;
}
}
// decode from word 2
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 15; i >= 0; i--) {
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return false;
break;
}
}
// trailing parity (odd) test
uint8_t parity_sum = 0;
for(int8_t i = 0; i < 13; i++) {
if(((result >> i) & 1) == 1) {
parity_sum++;
}
}
if((parity_sum % 2) != 1) {
return false;
}
// leading parity (even) test
parity_sum = 0;
for(int8_t i = 13; i < 26; i++) {
if(((result >> i) & 1) == 1) {
parity_sum++;
}
}
if((parity_sum % 2) == 1) {
return false;
}
return true;
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "protocol_generic.h"
class ProtocolHID10301 : public ProtocolGeneric {
public:
uint8_t get_encoded_data_size() final;
uint8_t get_decoded_data_size() final;
void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) final;
void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) final;
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
};

View File

@ -1,237 +0,0 @@
#include "protocol_indala_40134.h"
#include <furi.h>
typedef uint64_t Indala40134CardData;
static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) {
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
if(bit) {
*card_data |= 1ull << position;
} else {
*card_data &= ~(1ull << position);
}
}
static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
return (*card_data >> position) & 1;
}
uint8_t ProtocolIndala40134::get_encoded_data_size() {
return sizeof(Indala40134CardData);
}
uint8_t ProtocolIndala40134::get_decoded_data_size() {
return 3;
}
void ProtocolIndala40134::encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
Indala40134CardData card_data = 0;
// preamble
set_bit(1, 0, &card_data);
set_bit(1, 2, &card_data);
set_bit(1, 32, &card_data);
// factory code
set_bit(((fc_and_card >> 23) & 1), 57, &card_data);
set_bit(((fc_and_card >> 22) & 1), 49, &card_data);
set_bit(((fc_and_card >> 21) & 1), 44, &card_data);
set_bit(((fc_and_card >> 20) & 1), 47, &card_data);
set_bit(((fc_and_card >> 19) & 1), 48, &card_data);
set_bit(((fc_and_card >> 18) & 1), 53, &card_data);
set_bit(((fc_and_card >> 17) & 1), 39, &card_data);
set_bit(((fc_and_card >> 16) & 1), 58, &card_data);
// card number
set_bit(((fc_and_card >> 15) & 1), 42, &card_data);
set_bit(((fc_and_card >> 14) & 1), 45, &card_data);
set_bit(((fc_and_card >> 13) & 1), 43, &card_data);
set_bit(((fc_and_card >> 12) & 1), 40, &card_data);
set_bit(((fc_and_card >> 11) & 1), 52, &card_data);
set_bit(((fc_and_card >> 10) & 1), 36, &card_data);
set_bit(((fc_and_card >> 9) & 1), 35, &card_data);
set_bit(((fc_and_card >> 8) & 1), 51, &card_data);
set_bit(((fc_and_card >> 7) & 1), 46, &card_data);
set_bit(((fc_and_card >> 6) & 1), 33, &card_data);
set_bit(((fc_and_card >> 5) & 1), 37, &card_data);
set_bit(((fc_and_card >> 4) & 1), 54, &card_data);
set_bit(((fc_and_card >> 3) & 1), 56, &card_data);
set_bit(((fc_and_card >> 2) & 1), 59, &card_data);
set_bit(((fc_and_card >> 1) & 1), 50, &card_data);
set_bit(((fc_and_card >> 0) & 1), 41, &card_data);
// checksum
uint8_t checksum = 0;
checksum += ((fc_and_card >> 14) & 1);
checksum += ((fc_and_card >> 12) & 1);
checksum += ((fc_and_card >> 9) & 1);
checksum += ((fc_and_card >> 8) & 1);
checksum += ((fc_and_card >> 6) & 1);
checksum += ((fc_and_card >> 5) & 1);
checksum += ((fc_and_card >> 2) & 1);
checksum += ((fc_and_card >> 0) & 1);
// wiegand parity bits
// even parity sum calculation (high 12 bits of data)
uint8_t even_parity_sum = 0;
for(int8_t i = 12; i < 24; i++) {
if(((fc_and_card >> i) & 1) == 1) {
even_parity_sum++;
}
}
// odd parity sum calculation (low 12 bits of data)
uint8_t odd_parity_sum = 1;
for(int8_t i = 0; i < 12; i++) {
if(((fc_and_card >> i) & 1) == 1) {
odd_parity_sum++;
}
}
// even parity bit
set_bit((even_parity_sum % 2), 34, &card_data);
// odd parity bit
set_bit((odd_parity_sum % 2), 38, &card_data);
// checksum
if((checksum & 1) == 1) {
set_bit(0, 62, &card_data);
set_bit(1, 63, &card_data);
} else {
set_bit(1, 62, &card_data);
set_bit(0, 63, &card_data);
}
memcpy(encoded_data, &card_data, get_encoded_data_size());
}
// factory code
static uint8_t get_fc(const Indala40134CardData* card_data) {
uint8_t fc = 0;
fc = fc << 1 | get_bit(57, card_data);
fc = fc << 1 | get_bit(49, card_data);
fc = fc << 1 | get_bit(44, card_data);
fc = fc << 1 | get_bit(47, card_data);
fc = fc << 1 | get_bit(48, card_data);
fc = fc << 1 | get_bit(53, card_data);
fc = fc << 1 | get_bit(39, card_data);
fc = fc << 1 | get_bit(58, card_data);
return fc;
}
// card number
static uint16_t get_cn(const Indala40134CardData* card_data) {
uint16_t cn = 0;
cn = cn << 1 | get_bit(42, card_data);
cn = cn << 1 | get_bit(45, card_data);
cn = cn << 1 | get_bit(43, card_data);
cn = cn << 1 | get_bit(40, card_data);
cn = cn << 1 | get_bit(52, card_data);
cn = cn << 1 | get_bit(36, card_data);
cn = cn << 1 | get_bit(35, card_data);
cn = cn << 1 | get_bit(51, card_data);
cn = cn << 1 | get_bit(46, card_data);
cn = cn << 1 | get_bit(33, card_data);
cn = cn << 1 | get_bit(37, card_data);
cn = cn << 1 | get_bit(54, card_data);
cn = cn << 1 | get_bit(56, card_data);
cn = cn << 1 | get_bit(59, card_data);
cn = cn << 1 | get_bit(50, card_data);
cn = cn << 1 | get_bit(41, card_data);
return cn;
}
void ProtocolIndala40134::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
uint8_t fc = get_fc(card_data);
uint16_t card = get_cn(card_data);
decoded_data[0] = fc;
decoded_data[1] = card >> 8;
decoded_data[2] = card;
}
bool ProtocolIndala40134::can_be_decoded(
const uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
bool can_be_decoded = false;
const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
do {
// preambula
if((*card_data >> 32) != 0xa0000000UL) break;
// data
const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
// checksum
const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
uint8_t checksum_sum = 0;
checksum_sum += ((fc_and_card >> 14) & 1);
checksum_sum += ((fc_and_card >> 12) & 1);
checksum_sum += ((fc_and_card >> 9) & 1);
checksum_sum += ((fc_and_card >> 8) & 1);
checksum_sum += ((fc_and_card >> 6) & 1);
checksum_sum += ((fc_and_card >> 5) & 1);
checksum_sum += ((fc_and_card >> 2) & 1);
checksum_sum += ((fc_and_card >> 0) & 1);
checksum_sum = checksum_sum & 0b1;
if(checksum_sum == 1 && checksum == 0b01) {
} else if(checksum_sum == 0 && checksum == 0b10) {
} else {
break;
}
// wiegand parity bits
// even parity sum calculation (high 12 bits of data)
const bool even_parity = get_bit(34, card_data);
uint8_t even_parity_sum = 0;
for(int8_t i = 12; i < 24; i++) {
if(((fc_and_card >> i) & 1) == 1) {
even_parity_sum++;
}
}
if(even_parity_sum % 2 != even_parity) break;
// odd parity sum calculation (low 12 bits of data)
const bool odd_parity = get_bit(38, card_data);
uint8_t odd_parity_sum = 1;
for(int8_t i = 0; i < 12; i++) {
if(((fc_and_card >> i) & 1) == 1) {
odd_parity_sum++;
}
}
if(odd_parity_sum % 2 != odd_parity) break;
can_be_decoded = true;
} while(false);
return can_be_decoded;
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "protocol_generic.h"
class ProtocolIndala40134 : public ProtocolGeneric {
public:
uint8_t get_encoded_data_size() final;
uint8_t get_decoded_data_size() final;
void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) final;
void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) final;
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
};

View File

@ -1,193 +0,0 @@
#include "protocol_ioprox.h"
#include <furi.h>
#include <cli/cli.h>
/**
* Writes a bit into the output buffer.
*/
static void write_bit(bool bit, uint8_t position, uint8_t* data) {
if(bit) {
data[position / 8] |= 1UL << (7 - (position % 8));
} else {
data[position / 8] &= ~(1UL << (7 - (position % 8)));
}
}
/**
* Writes up to eight contiguous bits into the output buffer.
*/
static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) {
furi_check(length <= 8);
furi_check(length > 0);
for(uint8_t i = 0; i < length; ++i) {
uint8_t shift = 7 - i;
write_bit((byte >> shift) & 1, position + i, data);
}
}
uint8_t ProtocolIoProx::get_encoded_data_size() {
return 8;
}
uint8_t ProtocolIoProx::get_decoded_data_size() {
return 4;
}
void ProtocolIoProx::encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
// Packet to transmit:
//
// 0 10 20 30 40 50 60
// v v v v v v v
// 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23
// -----------------------------------------------------------------------------
// 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11
// Preamble.
write_bits(0b00000000, 0, encoded_data, 8);
write_bit(0, 8, encoded_data);
write_bits(0b11110000, 9, encoded_data, 8);
write_bit(1, 17, encoded_data);
// Facility code.
write_bits(decoded_data[0], 18, encoded_data, 8);
write_bit(1, 26, encoded_data);
// Version
write_bits(decoded_data[1], 27, encoded_data, 8);
write_bit(1, 35, encoded_data);
// Code one
write_bits(decoded_data[2], 36, encoded_data, 8);
write_bit(1, 44, encoded_data);
// Code two
write_bits(decoded_data[3], 45, encoded_data, 8);
write_bit(1, 53, encoded_data);
// Checksum
write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8);
write_bit(1, 62, encoded_data);
write_bit(1, 63, encoded_data);
}
void ProtocolIoProx::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
// Packet structure:
// (Note: the second word seems fixed; but this may not be a guarantee;
// it currently has no meaning.)
//
//0 1 2 3 4 5 6 7
//v v v v v v v v
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
//-----------------------------------------------------------------------
//00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11
//
// F = facility code
// V = version
// C = code
// X = checksum
// Facility code
decoded_data[0] = (encoded_data[2] << 2) | (encoded_data[3] >> 6);
// Version code.
decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5);
// Code bytes.
decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4);
decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3);
}
bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
// Packet framing
//
//0 1 2 3 4 5 6 7
//v v v v v v v v
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
//-----------------------------------------------------------------------
//00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11
//
// _ = variable data
// 0 = preamble 0
// 1 = framing 1
// X = checksum
// Validate the packet preamble is there...
if(encoded_data[0] != 0b00000000) {
return false;
}
if((encoded_data[1] >> 6) != 0b01) {
return false;
}
// ... check for known ones...
if((encoded_data[2] & 0b01000000) == 0) {
return false;
}
if((encoded_data[3] & 0b00100000) == 0) {
return false;
}
if((encoded_data[4] & 0b00010000) == 0) {
return false;
}
if((encoded_data[5] & 0b00001000) == 0) {
return false;
}
if((encoded_data[6] & 0b00000100) == 0) {
return false;
}
if((encoded_data[7] & 0b00000011) == 0) {
return false;
}
// ... and validate our checksums.
uint8_t checksum = compute_checksum(encoded_data, 8);
uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2);
if(checksum != checkval) {
return false;
}
return true;
}
uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) {
furi_check(data_size == get_encoded_data_size());
// Packet structure:
//
//0 1 2 3 4 5 6 7
//v v v v v v v v
//01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF
//00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11
//
// algorithm as observed by the proxmark3 folks
// CHECKSUM == 0xFF - (V + W + X + Y + Z)
uint8_t checksum = 0;
checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV
checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW
checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX
checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY
checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ
return 0xFF - checksum;
}

View File

@ -1,26 +0,0 @@
#pragma once
#include "protocol_generic.h"
class ProtocolIoProx : public ProtocolGeneric {
public:
uint8_t get_encoded_data_size() final;
uint8_t get_decoded_data_size() final;
void encode(
const uint8_t* decoded_data,
const uint8_t decoded_data_size,
uint8_t* encoded_data,
const uint8_t encoded_data_size) final;
void decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) final;
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
private:
/** Computes the IoProx checksum of the provided (decoded) data. */
uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size);
};

View File

@ -1,95 +0,0 @@
#include "pulse_joiner.h"
#include <furi.h>
bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) {
bool result = false;
furi_check((pulse_index + 1) < pulse_max);
if(polarity == false && pulse_index == 0) {
// first negative pulse is ommited
} else {
pulses[pulse_index].polarity = polarity;
pulses[pulse_index].time = pulse;
pulse_index++;
}
if(period > pulse) {
pulses[pulse_index].polarity = !polarity;
pulses[pulse_index].time = period - pulse;
pulse_index++;
}
if(pulse_index >= 4) {
// we know that first pulse is always high
// so we wait 2 edges, hi2low and next low2hi
uint8_t edges_count = 0;
bool last_polarity = pulses[0].polarity;
for(uint8_t i = 1; i < pulse_index; i++) {
if(pulses[i].polarity != last_polarity) {
edges_count++;
last_polarity = pulses[i].polarity;
}
}
if(edges_count >= 2) {
result = true;
}
}
return result;
}
void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) {
furi_check(pulse_index <= (pulse_max + 1));
uint16_t tmp_period = 0;
uint16_t tmp_pulse = 0;
uint8_t edges_count = 0;
bool last_polarity = pulses[0].polarity;
uint8_t next_fist_pulse = 0;
for(uint8_t i = 0; i < pulse_max; i++) {
// count edges
if(pulses[i].polarity != last_polarity) {
edges_count++;
last_polarity = pulses[i].polarity;
}
// wait for 2 edges
if(edges_count == 2) {
next_fist_pulse = i;
break;
}
// sum pulse time
if(pulses[i].polarity) {
tmp_period += pulses[i].time;
tmp_pulse += pulses[i].time;
} else {
tmp_period += pulses[i].time;
}
pulse_index--;
}
*period = tmp_period;
*pulse = tmp_pulse;
// remove counted periods and shift data
for(uint8_t i = 0; i < pulse_max; i++) {
if((next_fist_pulse + i) < pulse_max) {
pulses[i].polarity = pulses[next_fist_pulse + i].polarity;
pulses[i].time = pulses[next_fist_pulse + i].time;
} else {
break;
}
}
}
PulseJoiner::PulseJoiner() {
for(uint8_t i = 0; i < pulse_max; i++) {
pulses[i] = {false, 0};
}
}

View File

@ -1,36 +0,0 @@
#pragma once
#include "stdint.h"
class PulseJoiner {
public:
/**
* @brief Push timer pulse. First negative pulse is ommited.
*
* @param polarity pulse polarity: true = high2low, false = low2high
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*
* @return true - next pulse can and must be popped immediatly
*/
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
/**
* @brief Get the next timer pulse. Call only if push_pulse returns true.
*
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*/
void pop_pulse(uint16_t* period, uint16_t* pulse);
PulseJoiner();
private:
struct Pulse {
bool polarity;
uint16_t time;
};
uint8_t pulse_index = 0;
static const uint8_t pulse_max = 6;
Pulse pulses[pulse_max];
};

View File

@ -1,65 +0,0 @@
#include "rfid_key.h"
#include <core/check.h>
#include <string.h>
RfidKey::RfidKey() {
clear();
}
RfidKey::~RfidKey() {
}
void RfidKey::set_type(LfrfidKeyType _type) {
type = _type;
}
void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) {
furi_assert(_data_size <= data.size());
for(uint8_t i = 0; i < _data_size; i++) {
data[i] = _data[i];
}
}
void RfidKey::set_name(const char* _name) {
strlcpy(name, _name, get_name_length());
}
LfrfidKeyType RfidKey::get_type() {
return type;
}
const uint8_t* RfidKey::get_data() {
return &data[0];
}
const char* RfidKey::get_type_text() {
return lfrfid_key_get_type_string(type);
}
uint8_t RfidKey::get_type_data_count() const {
return lfrfid_key_get_type_data_count(type);
}
char* RfidKey::get_name() {
return name;
}
uint8_t RfidKey::get_name_length() {
return LFRFID_KEY_NAME_SIZE;
}
void RfidKey::clear() {
set_name("");
set_type(LfrfidKeyType::KeyEM4100);
data.fill(0);
}
RfidKey& RfidKey::operator=(const RfidKey& rhs) {
if(this == &rhs) return *this;
set_type(rhs.type);
set_name(rhs.name);
set_data(&rhs.data[0], get_type_data_count());
return *this;
}

View File

@ -1,27 +0,0 @@
#pragma once
#include "key_info.h"
#include <array>
class RfidKey {
public:
RfidKey();
~RfidKey();
void set_type(LfrfidKeyType type);
void set_data(const uint8_t* data, const uint8_t data_size);
void set_name(const char* name);
LfrfidKeyType get_type();
const uint8_t* get_data();
const char* get_type_text();
uint8_t get_type_data_count() const;
char* get_name();
uint8_t get_name_length();
void clear();
RfidKey& operator=(const RfidKey& rhs);
private:
std::array<uint8_t, LFRFID_KEY_SIZE> data;
LfrfidKeyType type;
char name[LFRFID_KEY_NAME_SIZE + 1];
};

View File

@ -1,175 +0,0 @@
#include "rfid_reader.h"
#include <furi.h>
#include <furi_hal.h>
#include <stm32wbxx_ll_cortex.h>
/**
* @brief private violation assistant for RfidReader
*/
struct RfidReaderAccessor {
static void decode(RfidReader& rfid_reader, bool polarity) {
rfid_reader.decode(polarity);
}
};
void RfidReader::decode(bool polarity) {
uint32_t current_dwt_value = DWT->CYCCNT;
uint32_t period = current_dwt_value - last_dwt_value;
last_dwt_value = current_dwt_value;
#ifdef RFID_GPIO_DEBUG
decoder_gpio_out.process_front(polarity, period);
#endif
switch(type) {
case Type::Normal:
decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, period);
decoder_ioprox.process_front(polarity, period);
break;
case Type::Indala:
decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, period);
decoder_ioprox.process_front(polarity, period);
decoder_indala.process_front(polarity, period);
break;
}
detect_ticks++;
}
bool RfidReader::switch_timer_elapsed() {
const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f;
return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch;
}
void RfidReader::switch_timer_reset() {
switch_os_tick_last = furi_get_tick();
}
void RfidReader::switch_mode() {
switch(type) {
case Type::Normal:
type = Type::Indala;
furi_hal_rfid_change_read_config(62500.0f, 0.25f);
break;
case Type::Indala:
type = Type::Normal;
furi_hal_rfid_change_read_config(125000.0f, 0.5f);
break;
}
switch_timer_reset();
}
static void comparator_trigger_callback(bool level, void* comp_ctx) {
RfidReader* _this = static_cast<RfidReader*>(comp_ctx);
RfidReaderAccessor::decode(*_this, !level);
}
RfidReader::RfidReader() {
}
void RfidReader::start() {
type = Type::Normal;
furi_hal_rfid_pins_read();
furi_hal_rfid_tim_read(125000, 0.5);
furi_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_read_count = 0;
}
void RfidReader::start_forced(RfidReader::Type _type) {
start();
if(_type == Type::Indala) {
switch_mode();
}
}
void RfidReader::stop() {
furi_hal_rfid_pins_reset();
furi_hal_rfid_tim_read_stop();
furi_hal_rfid_tim_reset();
stop_comparator();
}
bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
bool result = false;
bool something_read = false;
// reading
if(decoder_em.read(data, data_size)) {
*_type = LfrfidKeyType::KeyEM4100;
something_read = true;
}
if(decoder_hid26.read(data, data_size)) {
*_type = LfrfidKeyType::KeyH10301;
something_read = true;
}
if(decoder_ioprox.read(data, data_size)) {
*_type = LfrfidKeyType::KeyIoProxXSF;
something_read = true;
}
if(decoder_indala.read(data, data_size)) {
*_type = LfrfidKeyType::KeyI40134;
something_read = true;
}
// validation
if(something_read) {
switch_timer_reset();
if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
last_read_count = last_read_count + 1;
if(last_read_count > 2) {
result = true;
}
} else {
last_read_type = *_type;
memcpy(last_read_data, data, data_size);
last_read_count = 0;
}
}
// mode switching
if(switch_enable && switch_timer_elapsed()) {
switch_mode();
last_read_count = 0;
}
return result;
}
bool RfidReader::detect() {
bool detected = false;
if(detect_ticks > 10) {
detected = true;
}
detect_ticks = 0;
return detected;
}
bool RfidReader::any_read() {
return last_read_count > 0;
}
void RfidReader::start_comparator(void) {
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this);
last_dwt_value = DWT->CYCCNT;
furi_hal_rfid_comp_start();
}
void RfidReader::stop_comparator(void) {
furi_hal_rfid_comp_stop();
furi_hal_rfid_comp_set_callback(NULL, NULL);
}

View File

@ -1,59 +0,0 @@
#pragma once
//#include "decoder_analyzer.h"
#include "decoder_gpio_out.h"
#include "decoder_emmarin.h"
#include "decoder_hid26.h"
#include "decoder_indala.h"
#include "decoder_ioprox.h"
#include "key_info.h"
//#define RFID_GPIO_DEBUG 1
class RfidReader {
public:
enum class Type : uint8_t {
Normal,
Indala,
};
RfidReader();
void start();
void start_forced(RfidReader::Type type);
void stop();
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true);
bool detect();
bool any_read();
private:
friend struct RfidReaderAccessor;
//DecoderAnalyzer decoder_analyzer;
#ifdef RFID_GPIO_DEBUG
DecoderGpioOut decoder_gpio_out;
#endif
DecoderEMMarin decoder_em;
DecoderHID26 decoder_hid26;
DecoderIndala decoder_indala;
DecoderIoProx decoder_ioprox;
uint32_t last_dwt_value;
void start_comparator(void);
void stop_comparator(void);
void decode(bool polarity);
uint32_t detect_ticks;
uint32_t switch_os_tick_last;
bool switch_timer_elapsed();
void switch_timer_reset();
void switch_mode();
LfrfidKeyType last_read_type;
uint8_t last_read_data[LFRFID_KEY_SIZE];
uint8_t last_read_count;
Type type = Type::Normal;
};

View File

@ -1,56 +0,0 @@
#include "rfid_timer_emulator.h"
RfidTimerEmulator::RfidTimerEmulator() {
}
RfidTimerEmulator::~RfidTimerEmulator() {
std::map<LfrfidKeyType, EncoderGeneric*>::iterator it;
for(it = encoders.begin(); it != encoders.end(); ++it) {
delete it->second;
}
encoders.clear();
}
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
if(encoders.count(type)) {
current_encoder = encoders.find(type)->second;
if(data_size >= lfrfid_key_get_type_data_count(type)) {
current_encoder->init(data, data_size);
furi_hal_rfid_tim_emulate(125000);
furi_hal_rfid_pins_emulate();
furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this);
}
} else {
// not found
}
}
void RfidTimerEmulator::stop() {
furi_hal_rfid_tim_emulate_stop();
furi_hal_rfid_tim_reset();
furi_hal_rfid_pins_reset();
}
void RfidTimerEmulator::timer_update_callback(void* ctx) {
RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
bool result;
bool polarity;
uint16_t period;
uint16_t pulse;
do {
_this->current_encoder->get_next(&polarity, &period, &pulse);
result = _this->pulse_joiner.push_pulse(polarity, period, pulse);
} while(result == false);
_this->pulse_joiner.pop_pulse(&period, &pulse);
furi_hal_rfid_set_emulate_period(period - 1);
furi_hal_rfid_set_emulate_pulse(pulse);
}

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