Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -42,6 +42,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm
 | 
					/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Assets
 | 
					# Assets
 | 
				
			||||||
/assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
 | 
					/assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -60,8 +60,9 @@ jobs:
 | 
				
			|||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          set -e
 | 
					          set -e
 | 
				
			||||||
          for TARGET in ${TARGETS}; do
 | 
					          for TARGET in ${TARGETS}; do
 | 
				
			||||||
                ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
 | 
					            TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
 | 
				
			||||||
                copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
 | 
					            ./fbt TARGET_HW=$TARGET copro_dist updater_package \
 | 
				
			||||||
 | 
					            ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
 | 
				
			||||||
          done
 | 
					          done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: 'Move upload files'
 | 
					      - name: 'Move upload files'
 | 
				
			||||||
@ -186,6 +187,6 @@ jobs:
 | 
				
			|||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          set -e
 | 
					          set -e
 | 
				
			||||||
          for TARGET in ${TARGETS}; do
 | 
					          for TARGET in ${TARGETS}; do
 | 
				
			||||||
                ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
 | 
					            TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
 | 
				
			||||||
                updater_package DEBUG=0 COMPACT=1
 | 
					            ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package
 | 
				
			||||||
          done
 | 
					          done
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -89,6 +89,6 @@ jobs:
 | 
				
			|||||||
      - name: 'Raise exception'
 | 
					      - name: 'Raise exception'
 | 
				
			||||||
        if: ${{ steps.pvs-warn.outputs.warnings != 0 }}
 | 
					        if: ${{ steps.pvs-warn.outputs.warnings != 0 }}
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          echo "Please fix all PVS varnings before merge"
 | 
					          echo "Please fix all PVS warnings before merge"
 | 
				
			||||||
          exit 1
 | 
					          exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,4 +1,5 @@
 | 
				
			|||||||
*.swp
 | 
					*.swp
 | 
				
			||||||
 | 
					*.swo
 | 
				
			||||||
*.gdb_history
 | 
					*.gdb_history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -11,5 +11,8 @@
 | 
				
			|||||||
		"augustocdias.tasks-shell-input"
 | 
							"augustocdias.tasks-shell-input"
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
	// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
 | 
						// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
 | 
				
			||||||
	"unwantedRecommendations": []
 | 
						"unwantedRecommendations": [
 | 
				
			||||||
}
 | 
							"twxs.cmake",
 | 
				
			||||||
 | 
							"ms-vscode.cmake-tools"
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ void AccessorApp::run(void) {
 | 
				
			|||||||
AccessorApp::AccessorApp()
 | 
					AccessorApp::AccessorApp()
 | 
				
			||||||
    : text_store{0} {
 | 
					    : text_store{0} {
 | 
				
			||||||
    notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
 | 
					    notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
 | 
				
			||||||
    onewire_host = onewire_host_alloc();
 | 
					    onewire_host = onewire_host_alloc(&ibutton_gpio);
 | 
				
			||||||
    furi_hal_power_enable_otg();
 | 
					    furi_hal_power_enable_otg();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="accessor",
 | 
					    appid="accessor",
 | 
				
			||||||
    name="Accessor",
 | 
					    name="Accessor",
 | 
				
			||||||
    apptype=FlipperAppType.DEBUG,
 | 
					    apptype=FlipperAppType.DEBUG,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="accessor_app",
 | 
					    entry_point="accessor_app",
 | 
				
			||||||
    cdefines=["APP_ACCESSOR"],
 | 
					    cdefines=["APP_ACCESSOR"],
 | 
				
			||||||
    requires=["gui"],
 | 
					    requires=["gui"],
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
#include "bt_carrier_test.h"
 | 
					#include "bt_carrier_test.h"
 | 
				
			||||||
#include "bt_test.h"
 | 
					#include "bt_test.h"
 | 
				
			||||||
#include "bt_test_types.h"
 | 
					#include "bt_test_types.h"
 | 
				
			||||||
#include "furi_hal_bt.h"
 | 
					#include <furi_hal_bt.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct BtCarrierTest {
 | 
					struct BtCarrierTest {
 | 
				
			||||||
    BtTest* bt_test;
 | 
					    BtTest* bt_test;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
#include "bt_packet_test.h"
 | 
					#include "bt_packet_test.h"
 | 
				
			||||||
#include "bt_test.h"
 | 
					#include "bt_test.h"
 | 
				
			||||||
#include "bt_test_types.h"
 | 
					#include "bt_test_types.h"
 | 
				
			||||||
#include "furi_hal_bt.h"
 | 
					#include <furi_hal_bt.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct BtPacketTest {
 | 
					struct BtPacketTest {
 | 
				
			||||||
    BtTest* bt_test;
 | 
					    BtTest* bt_test;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								applications/debug/example_custom_font/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					App(
 | 
				
			||||||
 | 
					    appid="example_custom_font",
 | 
				
			||||||
 | 
					    name="Example: custom font",
 | 
				
			||||||
 | 
					    apptype=FlipperAppType.EXTERNAL,
 | 
				
			||||||
 | 
					    entry_point="example_custom_font_main",
 | 
				
			||||||
 | 
					    requires=["gui"],
 | 
				
			||||||
 | 
					    stack_size=1 * 1024,
 | 
				
			||||||
 | 
					    fap_category="Debug",
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										98
									
								
								applications/debug/example_custom_font/example_custom_font.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gui/gui.h>
 | 
				
			||||||
 | 
					#include <input/input.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//This arrays contains the font itself. You can use any u8g2 font you want
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
 | 
				
			||||||
 | 
					Copyright: 
 | 
				
			||||||
 | 
					Glyphs: 95/203
 | 
				
			||||||
 | 
					BBX Build Mode: 0
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					const uint8_t u8g2_font_tom_thumb_4x6_tr[725] =
 | 
				
			||||||
 | 
					    "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310"
 | 
				
			||||||
 | 
					    "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1"
 | 
				
			||||||
 | 
					    "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244"
 | 
				
			||||||
 | 
					    "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60"
 | 
				
			||||||
 | 
					    "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227"
 | 
				
			||||||
 | 
					    "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227"
 | 
				
			||||||
 | 
					    "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32"
 | 
				
			||||||
 | 
					    "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3"
 | 
				
			||||||
 | 
					    "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0"
 | 
				
			||||||
 | 
					    "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12"
 | 
				
			||||||
 | 
					    "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227"
 | 
				
			||||||
 | 
					    "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310"
 | 
				
			||||||
 | 
					    "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$"
 | 
				
			||||||
 | 
					    "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U"
 | 
				
			||||||
 | 
					    "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^"
 | 
				
			||||||
 | 
					    "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7"
 | 
				
			||||||
 | 
					    "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35"
 | 
				
			||||||
 | 
					    "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T"
 | 
				
			||||||
 | 
					    "\1l\7\227\310\310\326\0m\7\223\310<R\0n\7\223\310\250d\5o\7\223\310U\252\2p\10\227"
 | 
				
			||||||
 | 
					    "\307\250\244V\4q\10\227\307-\225d\0r\6\223\310\315\22s\10\223\310\215\70\22\0t\10\227\310\245"
 | 
				
			||||||
 | 
					    "\25\243\0u\7\223\310$+\11v\10\223\310$\65R\2w\7\223\310\244q\4x\7\223\310\244\62\25"
 | 
				
			||||||
 | 
					    "y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
 | 
				
			||||||
 | 
					    "\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Screen is 128x64 px
 | 
				
			||||||
 | 
					static void app_draw_callback(Canvas* canvas, void* ctx) {
 | 
				
			||||||
 | 
					    UNUSED(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_clear(canvas);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_draw_str(canvas, 0, 6, "This is a tiny custom font");
 | 
				
			||||||
 | 
					    canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void app_input_callback(InputEvent* input_event, void* ctx) {
 | 
				
			||||||
 | 
					    furi_assert(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriMessageQueue* event_queue = ctx;
 | 
				
			||||||
 | 
					    furi_message_queue_put(event_queue, input_event, FuriWaitForever);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int32_t example_custom_font_main(void* p) {
 | 
				
			||||||
 | 
					    UNUSED(p);
 | 
				
			||||||
 | 
					    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure view port
 | 
				
			||||||
 | 
					    ViewPort* view_port = view_port_alloc();
 | 
				
			||||||
 | 
					    view_port_draw_callback_set(view_port, app_draw_callback, view_port);
 | 
				
			||||||
 | 
					    view_port_input_callback_set(view_port, app_input_callback, event_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Register view port in GUI
 | 
				
			||||||
 | 
					    Gui* gui = furi_record_open(RECORD_GUI);
 | 
				
			||||||
 | 
					    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    InputEvent event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool running = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while(running) {
 | 
				
			||||||
 | 
					        if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
 | 
				
			||||||
 | 
					            if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
 | 
				
			||||||
 | 
					                switch(event.key) {
 | 
				
			||||||
 | 
					                case InputKeyBack:
 | 
				
			||||||
 | 
					                    running = false;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    view_port_enabled_set(view_port, false);
 | 
				
			||||||
 | 
					    gui_remove_view_port(gui, view_port);
 | 
				
			||||||
 | 
					    view_port_free(view_port);
 | 
				
			||||||
 | 
					    furi_message_queue_free(event_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_record_close(RECORD_GUI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
#include <file_browser_test_icons.h>
 | 
					 | 
				
			||||||
#include "file_browser_app_i.h"
 | 
					#include "file_browser_app_i.h"
 | 
				
			||||||
#include "gui/modules/file_browser.h"
 | 
					#include <file_browser_test_icons.h>
 | 
				
			||||||
#include <furi.h>
 | 
					
 | 
				
			||||||
#include <furi_hal.h>
 | 
					#include <gui/modules/file_browser.h>
 | 
				
			||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
#include <lib/toolbox/path.h>
 | 
					#include <lib/toolbox/path.h>
 | 
				
			||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
 | 
					static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="lfrfid_debug",
 | 
					    appid="lfrfid_debug",
 | 
				
			||||||
    name="LF-RFID Debug",
 | 
					    name="LF-RFID Debug",
 | 
				
			||||||
    apptype=FlipperAppType.DEBUG,
 | 
					    apptype=FlipperAppType.DEBUG,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="lfrfid_debug_app",
 | 
					    entry_point="lfrfid_debug_app",
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
        "gui",
 | 
					        "gui",
 | 
				
			||||||
 | 
				
			|||||||
@ -348,13 +348,37 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
 | 
				
			|||||||
    memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
 | 
					    memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
 | 
					    MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
 | 
				
			||||||
    // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest])
 | 
					    // Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest])
 | 
				
			||||||
    uint8_t manufacturer_block[16] = {0};
 | 
					    uint8_t manufacturer_block[16] = {0};
 | 
				
			||||||
    memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
 | 
					    memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
 | 
				
			||||||
    mu_assert(
 | 
					    mu_assert(
 | 
				
			||||||
        memcmp(manufacturer_block, uid, uid_len) == 0,
 | 
					        memcmp(manufacturer_block, uid, uid_len) == 0,
 | 
				
			||||||
        "manufacturer_block uid doesn't match the file\r\n");
 | 
					        "manufacturer_block uid doesn't match the file\r\n");
 | 
				
			||||||
    for(uint8_t i = uid_len; i < 16; i++) {
 | 
					
 | 
				
			||||||
 | 
					    uint8_t position = 0;
 | 
				
			||||||
 | 
					    if(uid_len == 4) {
 | 
				
			||||||
 | 
					        position = uid_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uint8_t bcc = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for(int i = 0; i < uid_len; i++) {
 | 
				
			||||||
 | 
					            bcc ^= uid[i];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        position = uid_len - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(uint8_t i = position + 4; i < 16; i++) {
 | 
				
			||||||
        mu_assert(
 | 
					        mu_assert(
 | 
				
			||||||
            manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
 | 
					            manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    furi_check(rpc_session[0].session);
 | 
					    furi_check(rpc_session[0].session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1);
 | 
					    rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
 | 
				
			||||||
    rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
 | 
					    rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
 | 
				
			||||||
    rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
 | 
					    rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
 | 
				
			||||||
    rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
 | 
					    rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
 | 
				
			||||||
 | 
				
			|||||||
@ -12,8 +12,9 @@
 | 
				
			|||||||
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
 | 
					#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
 | 
				
			||||||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 | 
					#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 | 
				
			||||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 | 
					#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 | 
				
			||||||
 | 
					#define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n")
 | 
				
			||||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
 | 
					#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
 | 
				
			||||||
#define TEST_RANDOM_COUNT_PARSE 273
 | 
					#define TEST_RANDOM_COUNT_PARSE 329
 | 
				
			||||||
#define TEST_TIMEOUT 10000
 | 
					#define TEST_TIMEOUT 10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SubGhzEnvironment* environment_handler;
 | 
					static SubGhzEnvironment* environment_handler;
 | 
				
			||||||
@ -43,6 +44,8 @@ static void subghz_test_init(void) {
 | 
				
			|||||||
        environment_handler, CAME_ATOMO_DIR_NAME);
 | 
					        environment_handler, CAME_ATOMO_DIR_NAME);
 | 
				
			||||||
    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
					    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
				
			||||||
        environment_handler, NICE_FLOR_S_DIR_NAME);
 | 
					        environment_handler, NICE_FLOR_S_DIR_NAME);
 | 
				
			||||||
 | 
					    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
 | 
				
			||||||
 | 
					        environment_handler, ALUTECH_AT_4N_DIR_NAME);
 | 
				
			||||||
    subghz_environment_set_protocol_registry(
 | 
					    subghz_environment_set_protocol_registry(
 | 
				
			||||||
        environment_handler, (void*)&subghz_protocol_registry);
 | 
					        environment_handler, (void*)&subghz_protocol_registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -489,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
 | 
				
			|||||||
        "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_decoder_linear_delta3_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_decoder_test(
 | 
				
			||||||
 | 
					            EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"),
 | 
				
			||||||
 | 
					            SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),
 | 
				
			||||||
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MU_TEST(subghz_decoder_megacode_test) {
 | 
					MU_TEST(subghz_decoder_megacode_test) {
 | 
				
			||||||
    mu_assert(
 | 
					    mu_assert(
 | 
				
			||||||
        subghz_decoder_test(
 | 
					        subghz_decoder_test(
 | 
				
			||||||
@ -604,6 +615,36 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) {
 | 
				
			|||||||
        "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_decoder_dooya_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_decoder_test(
 | 
				
			||||||
 | 
					            EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
 | 
				
			||||||
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_decoder_alutech_at_4n_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_decoder_test(
 | 
				
			||||||
 | 
					            EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"),
 | 
				
			||||||
 | 
					            SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),
 | 
				
			||||||
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_decoder_nice_one_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_decoder_test(
 | 
				
			||||||
 | 
					            EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
 | 
				
			||||||
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_decoder_test(
 | 
				
			||||||
 | 
					            EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"),
 | 
				
			||||||
 | 
					            SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),
 | 
				
			||||||
 | 
					        "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//test encoders
 | 
					//test encoders
 | 
				
			||||||
MU_TEST(subghz_encoder_princeton_test) {
 | 
					MU_TEST(subghz_encoder_princeton_test) {
 | 
				
			||||||
    mu_assert(
 | 
					    mu_assert(
 | 
				
			||||||
@ -647,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
 | 
				
			|||||||
        "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
 | 
					        "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_encoder_linear_delta3_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")),
 | 
				
			||||||
 | 
					        "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MU_TEST(subghz_encoder_megacode_test) {
 | 
					MU_TEST(subghz_encoder_megacode_test) {
 | 
				
			||||||
    mu_assert(
 | 
					    mu_assert(
 | 
				
			||||||
        subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
 | 
					        subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
 | 
				
			||||||
@ -743,6 +790,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) {
 | 
				
			|||||||
        "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 | 
					        "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MU_TEST(subghz_encoder_dooya_test) {
 | 
				
			||||||
 | 
					    mu_assert(
 | 
				
			||||||
 | 
					        subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
 | 
				
			||||||
 | 
					        "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MU_TEST(subghz_random_test) {
 | 
					MU_TEST(subghz_random_test) {
 | 
				
			||||||
    mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
 | 
					    mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -772,6 +825,7 @@ MU_TEST_SUITE(subghz) {
 | 
				
			|||||||
    MU_RUN_TEST(subghz_decoder_somfy_telis_test);
 | 
					    MU_RUN_TEST(subghz_decoder_somfy_telis_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_star_line_test);
 | 
					    MU_RUN_TEST(subghz_decoder_star_line_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_linear_test);
 | 
					    MU_RUN_TEST(subghz_decoder_linear_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_decoder_linear_delta3_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_megacode_test);
 | 
					    MU_RUN_TEST(subghz_decoder_megacode_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_secplus_v1_test);
 | 
					    MU_RUN_TEST(subghz_decoder_secplus_v1_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_secplus_v2_test);
 | 
					    MU_RUN_TEST(subghz_decoder_secplus_v2_test);
 | 
				
			||||||
@ -788,6 +842,10 @@ MU_TEST_SUITE(subghz) {
 | 
				
			|||||||
    MU_RUN_TEST(subghz_decoder_ansonic_test);
 | 
					    MU_RUN_TEST(subghz_decoder_ansonic_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_smc5326_test);
 | 
					    MU_RUN_TEST(subghz_decoder_smc5326_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
 | 
					    MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_decoder_dooya_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_decoder_nice_one_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_princeton_test);
 | 
					    MU_RUN_TEST(subghz_encoder_princeton_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_came_test);
 | 
					    MU_RUN_TEST(subghz_encoder_came_test);
 | 
				
			||||||
@ -796,6 +854,7 @@ MU_TEST_SUITE(subghz) {
 | 
				
			|||||||
    MU_RUN_TEST(subghz_encoder_nice_flo_test);
 | 
					    MU_RUN_TEST(subghz_encoder_nice_flo_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_keelog_test);
 | 
					    MU_RUN_TEST(subghz_encoder_keelog_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_linear_test);
 | 
					    MU_RUN_TEST(subghz_encoder_linear_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_encoder_linear_delta3_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_megacode_test);
 | 
					    MU_RUN_TEST(subghz_encoder_megacode_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_holtek_test);
 | 
					    MU_RUN_TEST(subghz_encoder_holtek_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_secplus_v1_test);
 | 
					    MU_RUN_TEST(subghz_encoder_secplus_v1_test);
 | 
				
			||||||
@ -812,6 +871,7 @@ MU_TEST_SUITE(subghz) {
 | 
				
			|||||||
    MU_RUN_TEST(subghz_encoder_ansonic_test);
 | 
					    MU_RUN_TEST(subghz_encoder_ansonic_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_smc5326_test);
 | 
					    MU_RUN_TEST(subghz_encoder_smc5326_test);
 | 
				
			||||||
    MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
 | 
					    MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
 | 
				
			||||||
 | 
					    MU_RUN_TEST(subghz_encoder_dooya_test);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MU_RUN_TEST(subghz_random_test);
 | 
					    MU_RUN_TEST(subghz_random_test);
 | 
				
			||||||
    subghz_test_deinit();
 | 
					    subghz_test_deinit();
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ void minunit_print_progress() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void minunit_print_fail(const char* str) {
 | 
					void minunit_print_fail(const char* str) {
 | 
				
			||||||
    printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
 | 
					    printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
 | 
					void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								applications/examples/example_thermo/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					# 1-Wire Thermometer
 | 
				
			||||||
 | 
					This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. 
 | 
				
			||||||
 | 
					It also covers basic GUI, input handling, threads and localisation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Electrical connections
 | 
				
			||||||
 | 
					Before launching the application, connect the sensor to Flipper's external GPIO according to the table below:
 | 
				
			||||||
 | 
					| DS18B20 | Flipper |
 | 
				
			||||||
 | 
					| :-----: | :-----: |
 | 
				
			||||||
 | 
					| VDD | 9 |
 | 
				
			||||||
 | 
					| GND | 18 |
 | 
				
			||||||
 | 
					| DQ  | 17 |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*NOTE 1*: GND is also available on pins 8 and 11.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Launching the application
 | 
				
			||||||
 | 
					In order to launch this demo, follow the steps below:
 | 
				
			||||||
 | 
					1. Make sure your Flipper has an SD card installed.
 | 
				
			||||||
 | 
					2. Connect your Flipper to the computer via a USB cable.
 | 
				
			||||||
 | 
					3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Changing the data pin
 | 
				
			||||||
 | 
					It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```c
 | 
				
			||||||
 | 
					/* Possible GPIO pin choices:
 | 
				
			||||||
 | 
					 - gpio_ext_pc0
 | 
				
			||||||
 | 
					 - gpio_ext_pc1
 | 
				
			||||||
 | 
					 - gpio_ext_pc3
 | 
				
			||||||
 | 
					 - gpio_ext_pb2
 | 
				
			||||||
 | 
					 - gpio_ext_pb3
 | 
				
			||||||
 | 
					 - gpio_ext_pa4
 | 
				
			||||||
 | 
					 - gpio_ext_pa6
 | 
				
			||||||
 | 
					 - gpio_ext_pa7
 | 
				
			||||||
 | 
					 - ibutton_gpio
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define THERMO_GPIO_PIN (ibutton_gpio)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Do not forget about the external pull-up resistor as these pins do not have one built-in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With the changes been made, recompile and launch the application again. 
 | 
				
			||||||
 | 
					The on-screen text should reflect it by asking to connect the thermometer to another pin.
 | 
				
			||||||
							
								
								
									
										10
									
								
								applications/examples/example_thermo/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					App(
 | 
				
			||||||
 | 
					    appid="example_thermo",
 | 
				
			||||||
 | 
					    name="Example: Thermometer",
 | 
				
			||||||
 | 
					    apptype=FlipperAppType.EXTERNAL,
 | 
				
			||||||
 | 
					    entry_point="example_thermo_main",
 | 
				
			||||||
 | 
					    requires=["gui"],
 | 
				
			||||||
 | 
					    stack_size=1 * 1024,
 | 
				
			||||||
 | 
					    fap_icon="example_thermo_10px.png",
 | 
				
			||||||
 | 
					    fap_category="Examples",
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										356
									
								
								applications/examples/example_thermo/example_thermo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,356 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file contains an example application that reads and displays
 | 
				
			||||||
 | 
					 * the temperature from a DS18B20 1-wire thermometer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * It also covers basic GUI, input handling, threads and localisation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * References:
 | 
				
			||||||
 | 
					 * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gui/gui.h>
 | 
				
			||||||
 | 
					#include <gui/view_port.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <core/thread.h>
 | 
				
			||||||
 | 
					#include <core/kernel.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <locale/locale.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <one_wire/maxim_crc.h>
 | 
				
			||||||
 | 
					#include <one_wire/one_wire_host.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define UPDATE_PERIOD_MS 1000UL
 | 
				
			||||||
 | 
					#define TEXT_STORE_SIZE 64U
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DS18B20_CMD_CONVERT 0x44U
 | 
				
			||||||
 | 
					#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DS18B20_CFG_RESOLUTION_POS 5U
 | 
				
			||||||
 | 
					#define DS18B20_CFG_RESOLUTION_MASK 0x03U
 | 
				
			||||||
 | 
					#define DS18B20_DECIMAL_PART_MASK 0x0fU
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DS18B20_SIGN_MASK 0xf0U
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Possible GPIO pin choices:
 | 
				
			||||||
 | 
					 - gpio_ext_pc0
 | 
				
			||||||
 | 
					 - gpio_ext_pc1
 | 
				
			||||||
 | 
					 - gpio_ext_pc3
 | 
				
			||||||
 | 
					 - gpio_ext_pb2
 | 
				
			||||||
 | 
					 - gpio_ext_pb3
 | 
				
			||||||
 | 
					 - gpio_ext_pa4
 | 
				
			||||||
 | 
					 - gpio_ext_pa6
 | 
				
			||||||
 | 
					 - gpio_ext_pa7
 | 
				
			||||||
 | 
					 - ibutton_gpio
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define THERMO_GPIO_PIN (ibutton_gpio)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Flags which the reader thread responds to */
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    ReaderThreadFlagExit = 1,
 | 
				
			||||||
 | 
					} ReaderThreadFlag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef union {
 | 
				
			||||||
 | 
					    struct {
 | 
				
			||||||
 | 
					        uint8_t temp_lsb; /* Least significant byte of the temperature */
 | 
				
			||||||
 | 
					        uint8_t temp_msb; /* Most significant byte of the temperature */
 | 
				
			||||||
 | 
					        uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */
 | 
				
			||||||
 | 
					        uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */
 | 
				
			||||||
 | 
					        uint8_t config; /* Configuration register */
 | 
				
			||||||
 | 
					        uint8_t reserved[3]; /* Not used */
 | 
				
			||||||
 | 
					        uint8_t crc; /* CRC checksum for error detection */
 | 
				
			||||||
 | 
					    } fields;
 | 
				
			||||||
 | 
					    uint8_t bytes[9];
 | 
				
			||||||
 | 
					} DS18B20Scratchpad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Application context structure */
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    Gui* gui;
 | 
				
			||||||
 | 
					    ViewPort* view_port;
 | 
				
			||||||
 | 
					    FuriThread* reader_thread;
 | 
				
			||||||
 | 
					    FuriMessageQueue* event_queue;
 | 
				
			||||||
 | 
					    OneWireHost* onewire;
 | 
				
			||||||
 | 
					    float temp_celsius;
 | 
				
			||||||
 | 
					    bool has_device;
 | 
				
			||||||
 | 
					} ExampleThermoContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*************** 1-Wire Communication and Processing *****************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Commands the thermometer to begin measuring the temperature. */
 | 
				
			||||||
 | 
					static void example_thermo_request_temperature(ExampleThermoContext* context) {
 | 
				
			||||||
 | 
					    OneWireHost* onewire = context->onewire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* All 1-wire transactions must happen in a critical section, i.e
 | 
				
			||||||
 | 
					       not interrupted by other threads. */
 | 
				
			||||||
 | 
					    FURI_CRITICAL_ENTER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool success = false;
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        /* Each communication with a 1-wire device starts by a reset.
 | 
				
			||||||
 | 
					           The functon will return true if a device responded with a presence pulse. */
 | 
				
			||||||
 | 
					        if(!onewire_host_reset(onewire)) break;
 | 
				
			||||||
 | 
					        /* After the reset, a ROM operation must follow.
 | 
				
			||||||
 | 
					           If there is only one device connected, the "Skip ROM" command is most appropriate
 | 
				
			||||||
 | 
					           (it can also be used to address all of the connected devices in some cases).*/
 | 
				
			||||||
 | 
					        onewire_host_skip(onewire);
 | 
				
			||||||
 | 
					        /* After the ROM operation, a device-specific command is issued.
 | 
				
			||||||
 | 
					           In this case, it's a request to start measuring the temperature. */
 | 
				
			||||||
 | 
					        onewire_host_write(onewire, DS18B20_CMD_CONVERT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        success = true;
 | 
				
			||||||
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->has_device = success;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FURI_CRITICAL_EXIT();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Reads the measured temperature from the thermometer. */
 | 
				
			||||||
 | 
					static void example_thermo_read_temperature(ExampleThermoContext* context) {
 | 
				
			||||||
 | 
					    /* If there was no device detected, don't try to read the temperature */
 | 
				
			||||||
 | 
					    if(!context->has_device) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OneWireHost* onewire = context->onewire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* All 1-wire transactions must happen in a critical section, i.e
 | 
				
			||||||
 | 
					       not interrupted by other threads. */
 | 
				
			||||||
 | 
					    FURI_CRITICAL_ENTER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool success = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        DS18B20Scratchpad buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Attempt reading the temperature 10 times before giving up */
 | 
				
			||||||
 | 
					        size_t attempts_left = 10;
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            /* Each communication with a 1-wire device starts by a reset.
 | 
				
			||||||
 | 
					            The functon will return true if a device responded with a presence pulse. */
 | 
				
			||||||
 | 
					            if(!onewire_host_reset(onewire)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* After the reset, a ROM operation must follow.
 | 
				
			||||||
 | 
					            If there is only one device connected, the "Skip ROM" command is most appropriate
 | 
				
			||||||
 | 
					            (it can also be used to address all of the connected devices in some cases).*/
 | 
				
			||||||
 | 
					            onewire_host_skip(onewire);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* After the ROM operation, a device-specific command is issued.
 | 
				
			||||||
 | 
					            This time, it will be the "Read Scratchpad" command which will
 | 
				
			||||||
 | 
					            prepare the device's internal buffer memory for reading. */
 | 
				
			||||||
 | 
					            onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* The actual reading happens here. A total of 9 bytes is read. */
 | 
				
			||||||
 | 
					            onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Calculate the checksum and compare it with one provided by the device. */
 | 
				
			||||||
 | 
					            const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Checksums match, exit the loop */
 | 
				
			||||||
 | 
					            if(crc == buf.fields.crc) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } while(--attempts_left);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(attempts_left == 0) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Get the measurement resolution from the configuration register. (See [1] page 9) */
 | 
				
			||||||
 | 
					        const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) &
 | 
				
			||||||
 | 
					                                        DS18B20_CFG_RESOLUTION_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */
 | 
				
			||||||
 | 
					        const uint8_t decimal_mask =
 | 
				
			||||||
 | 
					            (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) &
 | 
				
			||||||
 | 
					            DS18B20_DECIMAL_PART_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Get the integer and decimal part of the temperature (See [1] page 6) */
 | 
				
			||||||
 | 
					        const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U);
 | 
				
			||||||
 | 
					        const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Calculate the sign of the temperature (See [1] page 6) */
 | 
				
			||||||
 | 
					        const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Combine the integer and decimal part together */
 | 
				
			||||||
 | 
					        const float temp_celsius_abs = integer_part + decimal_part / 16.f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Set the appropriate sign */
 | 
				
			||||||
 | 
					        context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        success = true;
 | 
				
			||||||
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->has_device = success;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FURI_CRITICAL_EXIT();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */
 | 
				
			||||||
 | 
					static int32_t example_thermo_reader_thread_callback(void* ctx) {
 | 
				
			||||||
 | 
					    ExampleThermoContext* context = ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(;;) {
 | 
				
			||||||
 | 
					        /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */
 | 
				
			||||||
 | 
					        example_thermo_request_temperature(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Wait for the measurement to finish. At the same time wait for an exit signal. */
 | 
				
			||||||
 | 
					        const uint32_t flags =
 | 
				
			||||||
 | 
					            furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* If an exit signal was received, return from this thread. */
 | 
				
			||||||
 | 
					        if(flags != (unsigned)FuriFlagErrorTimeout) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* The measurement is now ready, read it from the termometer. */
 | 
				
			||||||
 | 
					        example_thermo_read_temperature(context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*************** GUI, Input and Main Loop *****************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Draw the GUI of the application. The screen is completely redrawn during each call. */
 | 
				
			||||||
 | 
					static void example_thermo_draw_callback(Canvas* canvas, void* ctx) {
 | 
				
			||||||
 | 
					    ExampleThermoContext* context = ctx;
 | 
				
			||||||
 | 
					    char text_store[TEXT_STORE_SIZE];
 | 
				
			||||||
 | 
					    const size_t middle_x = canvas_width(canvas) / 2U;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
 | 
					    canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo");
 | 
				
			||||||
 | 
					    canvas_draw_line(canvas, 0, 16, 128, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
 | 
					    canvas_draw_str_aligned(
 | 
				
			||||||
 | 
					        canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    snprintf(
 | 
				
			||||||
 | 
					        text_store,
 | 
				
			||||||
 | 
					        TEXT_STORE_SIZE,
 | 
				
			||||||
 | 
					        "to GPIO pin %ld",
 | 
				
			||||||
 | 
					        furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN));
 | 
				
			||||||
 | 
					    canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_set_font(canvas, FontKeyboard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(context->has_device) {
 | 
				
			||||||
 | 
					        float temp;
 | 
				
			||||||
 | 
					        char temp_units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* The applicaton is locale-aware.
 | 
				
			||||||
 | 
					           Change Settings->System->Units to check it out. */
 | 
				
			||||||
 | 
					        switch(locale_get_measurement_unit()) {
 | 
				
			||||||
 | 
					        case LocaleMeasurementUnitsMetric:
 | 
				
			||||||
 | 
					            temp = context->temp_celsius;
 | 
				
			||||||
 | 
					            temp_units = 'C';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case LocaleMeasurementUnitsImperial:
 | 
				
			||||||
 | 
					            temp = locale_celsius_to_fahrenheit(context->temp_celsius);
 | 
				
			||||||
 | 
					            temp_units = 'F';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            furi_crash("Illegal measurement units");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* If a reading is available, display it */
 | 
				
			||||||
 | 
					        snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* Or show a message that no data is available */
 | 
				
			||||||
 | 
					        strncpy(text_store, "-- No data --", TEXT_STORE_SIZE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This function is called from the GUI thread. All it does is put the event
 | 
				
			||||||
 | 
					   into the application's queue so it can be processed later. */
 | 
				
			||||||
 | 
					static void example_thermo_input_callback(InputEvent* event, void* ctx) {
 | 
				
			||||||
 | 
					    ExampleThermoContext* context = ctx;
 | 
				
			||||||
 | 
					    furi_message_queue_put(context->event_queue, event, FuriWaitForever);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Starts the reader thread and handles the input */
 | 
				
			||||||
 | 
					static void example_thermo_run(ExampleThermoContext* context) {
 | 
				
			||||||
 | 
					    /* Configure the hardware in host mode */
 | 
				
			||||||
 | 
					    onewire_host_start(context->onewire);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Start the reader thread. It will talk to the thermometer in the background. */
 | 
				
			||||||
 | 
					    furi_thread_start(context->reader_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* An endless loop which handles the input*/
 | 
				
			||||||
 | 
					    for(bool is_running = true; is_running;) {
 | 
				
			||||||
 | 
					        InputEvent event;
 | 
				
			||||||
 | 
					        /* Wait for an input event. Input events come from the GUI thread via a callback. */
 | 
				
			||||||
 | 
					        const FuriStatus status =
 | 
				
			||||||
 | 
					            furi_message_queue_get(context->event_queue, &event, FuriWaitForever);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* This application is only interested in short button presses. */
 | 
				
			||||||
 | 
					        if((status != FuriStatusOk) || (event.type != InputTypeShort)) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* When the user presses the "Back" button, break the loop and exit the application. */
 | 
				
			||||||
 | 
					        if(event.key == InputKeyBack) {
 | 
				
			||||||
 | 
					            is_running = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Signal the reader thread to cease operation and exit */
 | 
				
			||||||
 | 
					    furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the reader thread to finish */
 | 
				
			||||||
 | 
					    furi_thread_join(context->reader_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Reset the hardware */
 | 
				
			||||||
 | 
					    onewire_host_stop(context->onewire);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/******************** Initialisation & startup *****************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Allocate the memory and initialise the variables */
 | 
				
			||||||
 | 
					static ExampleThermoContext* example_thermo_context_alloc() {
 | 
				
			||||||
 | 
					    ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->view_port = view_port_alloc();
 | 
				
			||||||
 | 
					    view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context);
 | 
				
			||||||
 | 
					    view_port_input_callback_set(context->view_port, example_thermo_input_callback, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->reader_thread = furi_thread_alloc();
 | 
				
			||||||
 | 
					    furi_thread_set_stack_size(context->reader_thread, 1024U);
 | 
				
			||||||
 | 
					    furi_thread_set_context(context->reader_thread, context);
 | 
				
			||||||
 | 
					    furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->gui = furi_record_open(RECORD_GUI);
 | 
				
			||||||
 | 
					    gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return context;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Release the unused resources and deallocate memory */
 | 
				
			||||||
 | 
					static void example_thermo_context_free(ExampleThermoContext* context) {
 | 
				
			||||||
 | 
					    view_port_enabled_set(context->view_port, false);
 | 
				
			||||||
 | 
					    gui_remove_view_port(context->gui, context->view_port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onewire_host_free(context->onewire);
 | 
				
			||||||
 | 
					    furi_thread_free(context->reader_thread);
 | 
				
			||||||
 | 
					    furi_message_queue_free(context->event_queue);
 | 
				
			||||||
 | 
					    view_port_free(context->view_port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_record_close(RECORD_GUI);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The application's entry point. Execution starts from here. */
 | 
				
			||||||
 | 
					int32_t example_thermo_main(void* p) {
 | 
				
			||||||
 | 
					    UNUSED(p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Allocate all of the necessary structures */
 | 
				
			||||||
 | 
					    ExampleThermoContext* context = example_thermo_context_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Start the applicaton's main loop. It won't return until the application was requested to exit. */
 | 
				
			||||||
 | 
					    example_thermo_run(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Release all unneeded resources */
 | 
				
			||||||
 | 
					    example_thermo_context_free(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								applications/examples/example_thermo/example_thermo_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.1 KiB  | 
@ -1,10 +1,11 @@
 | 
				
			|||||||
#include <archive/views/archive_browser_view.h>
 | 
					 | 
				
			||||||
#include "archive_files.h"
 | 
					#include "archive_files.h"
 | 
				
			||||||
#include "archive_apps.h"
 | 
					#include "archive_apps.h"
 | 
				
			||||||
#include "archive_browser.h"
 | 
					#include "archive_browser.h"
 | 
				
			||||||
 | 
					#include "../views/archive_browser_view.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <core/common_defines.h>
 | 
					#include <core/common_defines.h>
 | 
				
			||||||
#include <core/log.h>
 | 
					#include <core/log.h>
 | 
				
			||||||
#include "gui/modules/file_browser_worker.h"
 | 
					#include <gui/modules/file_browser_worker.h>
 | 
				
			||||||
#include <fap_loader/fap_loader_app.h>
 | 
					#include <fap_loader/fap_loader_app.h>
 | 
				
			||||||
#include <math.h>
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,15 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../helpers/archive_files.h"
 | 
				
			||||||
 | 
					#include "../helpers/archive_favorites.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <gui/gui_i.h>
 | 
					#include <gui/gui_i.h>
 | 
				
			||||||
#include <gui/view.h>
 | 
					#include <gui/view.h>
 | 
				
			||||||
#include <gui/canvas.h>
 | 
					#include <gui/canvas.h>
 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
#include <furi.h>
 | 
					#include <gui/modules/file_browser_worker.h>
 | 
				
			||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
#include "../helpers/archive_files.h"
 | 
					#include <furi.h>
 | 
				
			||||||
#include "../helpers/archive_favorites.h"
 | 
					 | 
				
			||||||
#include "gui/modules/file_browser_worker.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_LEN_PX 110
 | 
					#define MAX_LEN_PX 110
 | 
				
			||||||
#define MAX_NAME_LEN 255
 | 
					#define MAX_NAME_LEN 255
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,12 @@
 | 
				
			|||||||
#include "bad_usb_app_i.h"
 | 
					#include "bad_usb_app_i.h"
 | 
				
			||||||
 | 
					#include "bad_usb_settings_filename.h"
 | 
				
			||||||
#include <furi.h>
 | 
					#include <furi.h>
 | 
				
			||||||
#include <furi_hal.h>
 | 
					#include <furi_hal.h>
 | 
				
			||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
#include <lib/toolbox/path.h>
 | 
					#include <lib/toolbox/path.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {
 | 
					static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
    BadUsbApp* app = context;
 | 
					    BadUsbApp* app = context;
 | 
				
			||||||
@ -22,15 +25,62 @@ static void bad_usb_app_tick_event_callback(void* context) {
 | 
				
			|||||||
    scene_manager_handle_tick_event(app->scene_manager);
 | 
					    scene_manager_handle_tick_event(app->scene_manager);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bad_usb_load_settings(BadUsbApp* app) {
 | 
				
			||||||
 | 
					    File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
 | 
				
			||||||
 | 
					    if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
				
			||||||
 | 
					        char chr;
 | 
				
			||||||
 | 
					        while((storage_file_read(settings_file, &chr, 1) == 1) &&
 | 
				
			||||||
 | 
					              !storage_file_eof(settings_file) && !isspace(chr)) {
 | 
				
			||||||
 | 
					            furi_string_push_back(app->keyboard_layout, chr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        furi_string_reset(app->keyboard_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    storage_file_close(settings_file);
 | 
				
			||||||
 | 
					    storage_file_free(settings_file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!furi_string_empty(app->keyboard_layout)) {
 | 
				
			||||||
 | 
					        Storage* fs_api = furi_record_open(RECORD_STORAGE);
 | 
				
			||||||
 | 
					        FileInfo layout_file_info;
 | 
				
			||||||
 | 
					        FS_Error file_check_err = storage_common_stat(
 | 
				
			||||||
 | 
					            fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
 | 
				
			||||||
 | 
					        furi_record_close(RECORD_STORAGE);
 | 
				
			||||||
 | 
					        if(file_check_err != FSE_OK) {
 | 
				
			||||||
 | 
					            furi_string_reset(app->keyboard_layout);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(layout_file_info.size != 256) {
 | 
				
			||||||
 | 
					            furi_string_reset(app->keyboard_layout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bad_usb_save_settings(BadUsbApp* app) {
 | 
				
			||||||
 | 
					    File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
 | 
				
			||||||
 | 
					    if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
 | 
				
			||||||
 | 
					        storage_file_write(
 | 
				
			||||||
 | 
					            settings_file,
 | 
				
			||||||
 | 
					            furi_string_get_cstr(app->keyboard_layout),
 | 
				
			||||||
 | 
					            furi_string_size(app->keyboard_layout));
 | 
				
			||||||
 | 
					        storage_file_write(settings_file, "\n", 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    storage_file_close(settings_file);
 | 
				
			||||||
 | 
					    storage_file_free(settings_file);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BadUsbApp* bad_usb_app_alloc(char* arg) {
 | 
					BadUsbApp* bad_usb_app_alloc(char* arg) {
 | 
				
			||||||
    BadUsbApp* app = malloc(sizeof(BadUsbApp));
 | 
					    BadUsbApp* app = malloc(sizeof(BadUsbApp));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app->file_path = furi_string_alloc();
 | 
					    app->bad_usb_script = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app->file_path = furi_string_alloc();
 | 
				
			||||||
 | 
					    app->keyboard_layout = furi_string_alloc();
 | 
				
			||||||
    if(arg && strlen(arg)) {
 | 
					    if(arg && strlen(arg)) {
 | 
				
			||||||
        furi_string_set(app->file_path, arg);
 | 
					        furi_string_set(app->file_path, arg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bad_usb_load_settings(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app->gui = furi_record_open(RECORD_GUI);
 | 
					    app->gui = furi_record_open(RECORD_GUI);
 | 
				
			||||||
    app->notifications = furi_record_open(RECORD_NOTIFICATION);
 | 
					    app->notifications = furi_record_open(RECORD_NOTIFICATION);
 | 
				
			||||||
    app->dialogs = furi_record_open(RECORD_DIALOGS);
 | 
					    app->dialogs = furi_record_open(RECORD_DIALOGS);
 | 
				
			||||||
@ -53,6 +103,10 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
 | 
				
			|||||||
    view_dispatcher_add_view(
 | 
					    view_dispatcher_add_view(
 | 
				
			||||||
        app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
 | 
					        app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app->submenu = submenu_alloc();
 | 
				
			||||||
 | 
					    view_dispatcher_add_view(
 | 
				
			||||||
 | 
					        app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app->bad_usb_view = bad_usb_alloc();
 | 
					    app->bad_usb_view = bad_usb_alloc();
 | 
				
			||||||
    view_dispatcher_add_view(
 | 
					    view_dispatcher_add_view(
 | 
				
			||||||
        app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view));
 | 
					        app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view));
 | 
				
			||||||
@ -64,9 +118,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
 | 
				
			|||||||
        scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
 | 
					        scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        if(!furi_string_empty(app->file_path)) {
 | 
					        if(!furi_string_empty(app->file_path)) {
 | 
				
			||||||
 | 
					            app->bad_usb_script = bad_usb_script_open(app->file_path);
 | 
				
			||||||
 | 
					            bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
 | 
					            scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            furi_string_set(app->file_path, BAD_USB_APP_PATH_FOLDER);
 | 
					            furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect);
 | 
					            scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -77,6 +133,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
 | 
				
			|||||||
void bad_usb_app_free(BadUsbApp* app) {
 | 
					void bad_usb_app_free(BadUsbApp* app) {
 | 
				
			||||||
    furi_assert(app);
 | 
					    furi_assert(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(app->bad_usb_script) {
 | 
				
			||||||
 | 
					        bad_usb_script_close(app->bad_usb_script);
 | 
				
			||||||
 | 
					        app->bad_usb_script = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Views
 | 
					    // Views
 | 
				
			||||||
    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
 | 
					    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
 | 
				
			||||||
    bad_usb_free(app->bad_usb_view);
 | 
					    bad_usb_free(app->bad_usb_view);
 | 
				
			||||||
@ -85,6 +146,10 @@ void bad_usb_app_free(BadUsbApp* app) {
 | 
				
			|||||||
    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
 | 
					    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
 | 
				
			||||||
    widget_free(app->widget);
 | 
					    widget_free(app->widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Submenu
 | 
				
			||||||
 | 
					    view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
 | 
				
			||||||
 | 
					    submenu_free(app->submenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // View dispatcher
 | 
					    // View dispatcher
 | 
				
			||||||
    view_dispatcher_free(app->view_dispatcher);
 | 
					    view_dispatcher_free(app->view_dispatcher);
 | 
				
			||||||
    scene_manager_free(app->scene_manager);
 | 
					    scene_manager_free(app->scene_manager);
 | 
				
			||||||
@ -94,7 +159,10 @@ void bad_usb_app_free(BadUsbApp* app) {
 | 
				
			|||||||
    furi_record_close(RECORD_NOTIFICATION);
 | 
					    furi_record_close(RECORD_NOTIFICATION);
 | 
				
			||||||
    furi_record_close(RECORD_DIALOGS);
 | 
					    furi_record_close(RECORD_DIALOGS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bad_usb_save_settings(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_string_free(app->file_path);
 | 
					    furi_string_free(app->file_path);
 | 
				
			||||||
 | 
					    furi_string_free(app->keyboard_layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    free(app);
 | 
					    free(app);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,8 +15,10 @@
 | 
				
			|||||||
#include <gui/modules/widget.h>
 | 
					#include <gui/modules/widget.h>
 | 
				
			||||||
#include "views/bad_usb_view.h"
 | 
					#include "views/bad_usb_view.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb")
 | 
					#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb")
 | 
				
			||||||
#define BAD_USB_APP_EXTENSION ".txt"
 | 
					#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts"
 | 
				
			||||||
 | 
					#define BAD_USB_APP_SCRIPT_EXTENSION ".txt"
 | 
				
			||||||
 | 
					#define BAD_USB_APP_LAYOUT_EXTENSION ".kl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    BadUsbAppErrorNoFiles,
 | 
					    BadUsbAppErrorNoFiles,
 | 
				
			||||||
@ -30,9 +32,11 @@ struct BadUsbApp {
 | 
				
			|||||||
    NotificationApp* notifications;
 | 
					    NotificationApp* notifications;
 | 
				
			||||||
    DialogsApp* dialogs;
 | 
					    DialogsApp* dialogs;
 | 
				
			||||||
    Widget* widget;
 | 
					    Widget* widget;
 | 
				
			||||||
 | 
					    Submenu* submenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BadUsbAppError error;
 | 
					    BadUsbAppError error;
 | 
				
			||||||
    FuriString* file_path;
 | 
					    FuriString* file_path;
 | 
				
			||||||
 | 
					    FuriString* keyboard_layout;
 | 
				
			||||||
    BadUsb* bad_usb_view;
 | 
					    BadUsb* bad_usb_view;
 | 
				
			||||||
    BadUsbScript* bad_usb_script;
 | 
					    BadUsbScript* bad_usb_script;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -40,4 +44,5 @@ struct BadUsbApp {
 | 
				
			|||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    BadUsbAppViewError,
 | 
					    BadUsbAppViewError,
 | 
				
			||||||
    BadUsbAppViewWork,
 | 
					    BadUsbAppViewWork,
 | 
				
			||||||
} BadUsbAppView;
 | 
					    BadUsbAppViewConfig,
 | 
				
			||||||
 | 
					} BadUsbAppView;
 | 
				
			||||||
@ -16,6 +16,9 @@
 | 
				
			|||||||
#define SCRIPT_STATE_END (-2)
 | 
					#define SCRIPT_STATE_END (-2)
 | 
				
			||||||
#define SCRIPT_STATE_NEXT_LINE (-3)
 | 
					#define SCRIPT_STATE_NEXT_LINE (-3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BADUSB_ASCII_TO_KEY(script, x) \
 | 
				
			||||||
 | 
					    (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    WorkerEvtToggle = (1 << 0),
 | 
					    WorkerEvtToggle = (1 << 0),
 | 
				
			||||||
    WorkerEvtEnd = (1 << 1),
 | 
					    WorkerEvtEnd = (1 << 1),
 | 
				
			||||||
@ -28,6 +31,7 @@ struct BadUsbScript {
 | 
				
			|||||||
    BadUsbState st;
 | 
					    BadUsbState st;
 | 
				
			||||||
    FuriString* file_path;
 | 
					    FuriString* file_path;
 | 
				
			||||||
    uint32_t defdelay;
 | 
					    uint32_t defdelay;
 | 
				
			||||||
 | 
					    uint16_t layout[128];
 | 
				
			||||||
    FuriThread* thread;
 | 
					    FuriThread* thread;
 | 
				
			||||||
    uint8_t file_buf[FILE_BUFFER_LEN + 1];
 | 
					    uint8_t file_buf[FILE_BUFFER_LEN + 1];
 | 
				
			||||||
    uint8_t buf_start;
 | 
					    uint8_t buf_start;
 | 
				
			||||||
@ -205,10 +209,10 @@ static bool ducky_altstring(const char* param) {
 | 
				
			|||||||
    return state;
 | 
					    return state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool ducky_string(const char* param) {
 | 
					static bool ducky_string(BadUsbScript* bad_usb, const char* param) {
 | 
				
			||||||
    uint32_t i = 0;
 | 
					    uint32_t i = 0;
 | 
				
			||||||
    while(param[i] != '\0') {
 | 
					    while(param[i] != '\0') {
 | 
				
			||||||
        uint16_t keycode = HID_ASCII_TO_KEY(param[i]);
 | 
					        uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);
 | 
				
			||||||
        if(keycode != HID_KEYBOARD_NONE) {
 | 
					        if(keycode != HID_KEYBOARD_NONE) {
 | 
				
			||||||
            furi_hal_hid_kb_press(keycode);
 | 
					            furi_hal_hid_kb_press(keycode);
 | 
				
			||||||
            furi_hal_hid_kb_release(keycode);
 | 
					            furi_hal_hid_kb_release(keycode);
 | 
				
			||||||
@ -218,7 +222,7 @@ static bool ducky_string(const char* param) {
 | 
				
			|||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
 | 
					static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
 | 
				
			||||||
    for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
 | 
					    for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
 | 
				
			||||||
        size_t key_cmd_len = strlen(ducky_keys[i].name);
 | 
					        size_t key_cmd_len = strlen(ducky_keys[i].name);
 | 
				
			||||||
        if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
 | 
					        if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
 | 
				
			||||||
@ -227,7 +231,7 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if((accept_chars) && (strlen(param) > 0)) {
 | 
					    if((accept_chars) && (strlen(param) > 0)) {
 | 
				
			||||||
        return (HID_ASCII_TO_KEY(param[0]) & 0xFF);
 | 
					        return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -276,7 +280,7 @@ static int32_t
 | 
				
			|||||||
    } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
 | 
					    } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
 | 
				
			||||||
        // STRING
 | 
					        // STRING
 | 
				
			||||||
        line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
					        line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
				
			||||||
        state = ducky_string(line_tmp);
 | 
					        state = ducky_string(bad_usb, line_tmp);
 | 
				
			||||||
        if(!state && error != NULL) {
 | 
					        if(!state && error != NULL) {
 | 
				
			||||||
            snprintf(error, error_len, "Invalid string %s", line_tmp);
 | 
					            snprintf(error, error_len, "Invalid string %s", line_tmp);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -312,14 +316,14 @@ static int32_t
 | 
				
			|||||||
    } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
 | 
					    } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
 | 
				
			||||||
        // SYSRQ
 | 
					        // SYSRQ
 | 
				
			||||||
        line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
					        line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
				
			||||||
        uint16_t key = ducky_get_keycode(line_tmp, true);
 | 
					        uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true);
 | 
				
			||||||
        furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
 | 
					        furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
 | 
				
			||||||
        furi_hal_hid_kb_press(key);
 | 
					        furi_hal_hid_kb_press(key);
 | 
				
			||||||
        furi_hal_hid_kb_release_all();
 | 
					        furi_hal_hid_kb_release_all();
 | 
				
			||||||
        return (0);
 | 
					        return (0);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Special keys + modifiers
 | 
					        // Special keys + modifiers
 | 
				
			||||||
        uint16_t key = ducky_get_keycode(line_tmp, false);
 | 
					        uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
 | 
				
			||||||
        if(key == HID_KEYBOARD_NONE) {
 | 
					        if(key == HID_KEYBOARD_NONE) {
 | 
				
			||||||
            if(error != NULL) {
 | 
					            if(error != NULL) {
 | 
				
			||||||
                snprintf(error, error_len, "No keycode defined for %s", line_tmp);
 | 
					                snprintf(error, error_len, "No keycode defined for %s", line_tmp);
 | 
				
			||||||
@ -329,7 +333,7 @@ static int32_t
 | 
				
			|||||||
        if((key & 0xFF00) != 0) {
 | 
					        if((key & 0xFF00) != 0) {
 | 
				
			||||||
            // It's a modifier key
 | 
					            // It's a modifier key
 | 
				
			||||||
            line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
					            line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
 | 
				
			||||||
            key |= ducky_get_keycode(line_tmp, true);
 | 
					            key |= ducky_get_keycode(bad_usb, line_tmp, true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        furi_hal_hid_kb_press(key);
 | 
					        furi_hal_hid_kb_press(key);
 | 
				
			||||||
        furi_hal_hid_kb_release(key);
 | 
					        furi_hal_hid_kb_release(key);
 | 
				
			||||||
@ -650,12 +654,19 @@ static int32_t bad_usb_worker(void* context) {
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
 | 
				
			||||||
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
 | 
					    memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout));
 | 
				
			||||||
 | 
					    memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
 | 
					BadUsbScript* bad_usb_script_open(FuriString* file_path) {
 | 
				
			||||||
    furi_assert(file_path);
 | 
					    furi_assert(file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
 | 
					    BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
 | 
				
			||||||
    bad_usb->file_path = furi_string_alloc();
 | 
					    bad_usb->file_path = furi_string_alloc();
 | 
				
			||||||
    furi_string_set(bad_usb->file_path, file_path);
 | 
					    furi_string_set(bad_usb->file_path, file_path);
 | 
				
			||||||
 | 
					    bad_usb_script_set_default_keyboard_layout(bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bad_usb->st.state = BadUsbStateInit;
 | 
					    bad_usb->st.state = BadUsbStateInit;
 | 
				
			||||||
    bad_usb->st.error[0] = '\0';
 | 
					    bad_usb->st.error[0] = '\0';
 | 
				
			||||||
@ -674,6 +685,30 @@ void bad_usb_script_close(BadUsbScript* bad_usb) {
 | 
				
			|||||||
    free(bad_usb);
 | 
					    free(bad_usb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) {
 | 
				
			||||||
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) {
 | 
				
			||||||
 | 
					        // do not update keyboard layout while a script is running
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
 | 
				
			||||||
 | 
					    if(!furi_string_empty(layout_path)) { //-V1051
 | 
				
			||||||
 | 
					        if(storage_file_open(
 | 
				
			||||||
 | 
					               layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
				
			||||||
 | 
					            uint16_t layout[128];
 | 
				
			||||||
 | 
					            if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
 | 
				
			||||||
 | 
					                memcpy(bad_usb->layout, layout, sizeof(layout));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        storage_file_close(layout_file);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        bad_usb_script_set_default_keyboard_layout(bad_usb);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    storage_file_free(layout_file);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_script_toggle(BadUsbScript* bad_usb) {
 | 
					void bad_usb_script_toggle(BadUsbScript* bad_usb) {
 | 
				
			||||||
    furi_assert(bad_usb);
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
    furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle);
 | 
					    furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle);
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,8 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void bad_usb_script_close(BadUsbScript* bad_usb);
 | 
					void bad_usb_script_close(BadUsbScript* bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_script_start(BadUsbScript* bad_usb);
 | 
					void bad_usb_script_start(BadUsbScript* bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_script_stop(BadUsbScript* bad_usb);
 | 
					void bad_usb_script_stop(BadUsbScript* bad_usb);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								applications/main/bad_usb/bad_usb_settings_filename.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings"
 | 
				
			||||||
							
								
								
									
										53
									
								
								applications/main/bad_usb/scenes/bad_usb_scene_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					#include "../bad_usb_app_i.h"
 | 
				
			||||||
 | 
					#include "furi_hal_power.h"
 | 
				
			||||||
 | 
					#include "furi_hal_usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum SubmenuIndex {
 | 
				
			||||||
 | 
					    SubmenuIndexKeyboardLayout,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) {
 | 
				
			||||||
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					    view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_scene_config_on_enter(void* context) {
 | 
				
			||||||
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					    Submenu* submenu = bad_usb->submenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submenu_add_item(
 | 
				
			||||||
 | 
					        submenu,
 | 
				
			||||||
 | 
					        "Keyboard layout",
 | 
				
			||||||
 | 
					        SubmenuIndexKeyboardLayout,
 | 
				
			||||||
 | 
					        bad_usb_scene_config_submenu_callback,
 | 
				
			||||||
 | 
					        bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
 | 
					        submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					    bool consumed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
 | 
					        scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event);
 | 
				
			||||||
 | 
					        consumed = true;
 | 
				
			||||||
 | 
					        if(event.event == SubmenuIndexKeyboardLayout) {
 | 
				
			||||||
 | 
					            scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            furi_crash("Unknown key type");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return consumed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_scene_config_on_exit(void* context) {
 | 
				
			||||||
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					    Submenu* submenu = bad_usb->submenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submenu_reset(submenu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,3 +1,5 @@
 | 
				
			|||||||
ADD_SCENE(bad_usb, file_select, FileSelect)
 | 
					ADD_SCENE(bad_usb, file_select, FileSelect)
 | 
				
			||||||
ADD_SCENE(bad_usb, work, Work)
 | 
					ADD_SCENE(bad_usb, work, Work)
 | 
				
			||||||
ADD_SCENE(bad_usb, error, Error)
 | 
					ADD_SCENE(bad_usb, error, Error)
 | 
				
			||||||
 | 
					ADD_SCENE(bad_usb, config, Config)
 | 
				
			||||||
 | 
					ADD_SCENE(bad_usb, config_layout, ConfigLayout)
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					#include "../bad_usb_app_i.h"
 | 
				
			||||||
 | 
					#include "furi_hal_power.h"
 | 
				
			||||||
 | 
					#include "furi_hal_usb.h"
 | 
				
			||||||
 | 
					#include <storage/storage.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool bad_usb_layout_select(BadUsbApp* bad_usb) {
 | 
				
			||||||
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriString* predefined_path;
 | 
				
			||||||
 | 
					    predefined_path = furi_string_alloc();
 | 
				
			||||||
 | 
					    if(!furi_string_empty(bad_usb->keyboard_layout)) {
 | 
				
			||||||
 | 
					        furi_string_set(predefined_path, bad_usb->keyboard_layout);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DialogsFileBrowserOptions browser_options;
 | 
				
			||||||
 | 
					    dialog_file_browser_set_basic_options(
 | 
				
			||||||
 | 
					        &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
 | 
				
			||||||
 | 
					    browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER;
 | 
				
			||||||
 | 
					    browser_options.skip_assets = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Input events and views are managed by file_browser
 | 
				
			||||||
 | 
					    bool res = dialog_file_browser_show(
 | 
				
			||||||
 | 
					        bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_string_free(predefined_path);
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_scene_config_layout_on_enter(void* context) {
 | 
				
			||||||
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(bad_usb_layout_select(bad_usb)) {
 | 
				
			||||||
 | 
					        bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    scene_manager_previous_scene(bad_usb->scene_manager);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					    UNUSED(event);
 | 
				
			||||||
 | 
					    // BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_scene_config_layout_on_exit(void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					    // BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,14 +1,16 @@
 | 
				
			|||||||
#include "../bad_usb_app_i.h"
 | 
					#include "../bad_usb_app_i.h"
 | 
				
			||||||
#include "furi_hal_power.h"
 | 
					#include <furi_hal_power.h>
 | 
				
			||||||
#include "furi_hal_usb.h"
 | 
					#include <furi_hal_usb.h>
 | 
				
			||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool bad_usb_file_select(BadUsbApp* bad_usb) {
 | 
					static bool bad_usb_file_select(BadUsbApp* bad_usb) {
 | 
				
			||||||
    furi_assert(bad_usb);
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DialogsFileBrowserOptions browser_options;
 | 
					    DialogsFileBrowserOptions browser_options;
 | 
				
			||||||
    dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px);
 | 
					    dialog_file_browser_set_basic_options(
 | 
				
			||||||
    browser_options.base_path = BAD_USB_APP_PATH_FOLDER;
 | 
					        &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px);
 | 
				
			||||||
 | 
					    browser_options.base_path = BAD_USB_APP_BASE_FOLDER;
 | 
				
			||||||
 | 
					    browser_options.skip_assets = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Input events and views are managed by file_browser
 | 
					    // Input events and views are managed by file_browser
 | 
				
			||||||
    bool res = dialog_file_browser_show(
 | 
					    bool res = dialog_file_browser_show(
 | 
				
			||||||
@ -21,12 +23,18 @@ void bad_usb_scene_file_select_on_enter(void* context) {
 | 
				
			|||||||
    BadUsbApp* bad_usb = context;
 | 
					    BadUsbApp* bad_usb = context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_hal_usb_disable();
 | 
					    furi_hal_usb_disable();
 | 
				
			||||||
 | 
					    if(bad_usb->bad_usb_script) {
 | 
				
			||||||
 | 
					        bad_usb_script_close(bad_usb->bad_usb_script);
 | 
				
			||||||
 | 
					        bad_usb->bad_usb_script = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(bad_usb_file_select(bad_usb)) {
 | 
					    if(bad_usb_file_select(bad_usb)) {
 | 
				
			||||||
 | 
					        bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path);
 | 
				
			||||||
 | 
					        bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
 | 
					        scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        furi_hal_usb_enable();
 | 
					        furi_hal_usb_enable();
 | 
				
			||||||
        //scene_manager_previous_scene(bad_usb->scene_manager);
 | 
					 | 
				
			||||||
        view_dispatcher_stop(bad_usb->view_dispatcher);
 | 
					        view_dispatcher_stop(bad_usb->view_dispatcher);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
#include "../bad_usb_script.h"
 | 
					#include "../bad_usb_script.h"
 | 
				
			||||||
#include "../bad_usb_app_i.h"
 | 
					#include "../bad_usb_app_i.h"
 | 
				
			||||||
#include "../views/bad_usb_view.h"
 | 
					#include "../views/bad_usb_view.h"
 | 
				
			||||||
#include "furi_hal.h"
 | 
					#include <furi_hal.h>
 | 
				
			||||||
#include "toolbox/path.h"
 | 
					#include "toolbox/path.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_scene_work_ok_callback(InputType type, void* context) {
 | 
					void bad_usb_scene_work_button_callback(InputKey key, void* context) {
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
    BadUsbApp* app = context;
 | 
					    BadUsbApp* app = context;
 | 
				
			||||||
    view_dispatcher_send_custom_event(app->view_dispatcher, type);
 | 
					    view_dispatcher_send_custom_event(app->view_dispatcher, key);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
 | 
					bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
@ -15,8 +15,13 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
    bool consumed = false;
 | 
					    bool consumed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        bad_usb_script_toggle(app->bad_usb_script);
 | 
					        if(event.event == InputKeyLeft) {
 | 
				
			||||||
        consumed = true;
 | 
					            scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
 | 
				
			||||||
 | 
					            consumed = true;
 | 
				
			||||||
 | 
					        } else if(event.event == InputKeyOk) {
 | 
				
			||||||
 | 
					            bad_usb_script_toggle(app->bad_usb_script);
 | 
				
			||||||
 | 
					            consumed = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    } else if(event.type == SceneManagerEventTypeTick) {
 | 
					    } else if(event.type == SceneManagerEventTypeTick) {
 | 
				
			||||||
        bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
 | 
					        bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -28,20 +33,22 @@ void bad_usb_scene_work_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    FuriString* file_name;
 | 
					    FuriString* file_name;
 | 
				
			||||||
    file_name = furi_string_alloc();
 | 
					    file_name = furi_string_alloc();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    path_extract_filename(app->file_path, file_name, true);
 | 
					    path_extract_filename(app->file_path, file_name, true);
 | 
				
			||||||
    bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name));
 | 
					    bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name));
 | 
				
			||||||
    app->bad_usb_script = bad_usb_script_open(app->file_path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_string_free(file_name);
 | 
					    furi_string_free(file_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FuriString* layout;
 | 
				
			||||||
 | 
					    layout = furi_string_alloc();
 | 
				
			||||||
 | 
					    path_extract_filename(app->keyboard_layout, layout, true);
 | 
				
			||||||
 | 
					    bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout));
 | 
				
			||||||
 | 
					    furi_string_free(layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
 | 
					    bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app);
 | 
					    bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app);
 | 
				
			||||||
    view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork);
 | 
					    view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_scene_work_on_exit(void* context) {
 | 
					void bad_usb_scene_work_on_exit(void* context) {
 | 
				
			||||||
    BadUsbApp* app = context;
 | 
					    UNUSED(context);
 | 
				
			||||||
    bad_usb_script_close(app->bad_usb_script);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
#include "bad_usb_view.h"
 | 
					#include "bad_usb_view.h"
 | 
				
			||||||
#include "../bad_usb_script.h"
 | 
					#include "../bad_usb_script.h"
 | 
				
			||||||
 | 
					#include <toolbox/path.h>
 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
#include <assets_icons.h>
 | 
					#include <assets_icons.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -7,12 +8,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct BadUsb {
 | 
					struct BadUsb {
 | 
				
			||||||
    View* view;
 | 
					    View* view;
 | 
				
			||||||
    BadUsbOkCallback callback;
 | 
					    BadUsbButtonCallback callback;
 | 
				
			||||||
    void* context;
 | 
					    void* context;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    char file_name[MAX_NAME_LEN];
 | 
					    char file_name[MAX_NAME_LEN];
 | 
				
			||||||
 | 
					    char layout[MAX_NAME_LEN];
 | 
				
			||||||
    BadUsbState state;
 | 
					    BadUsbState state;
 | 
				
			||||||
    uint8_t anim_frame;
 | 
					    uint8_t anim_frame;
 | 
				
			||||||
} BadUsbModel;
 | 
					} BadUsbModel;
 | 
				
			||||||
@ -25,9 +27,23 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
 | 
				
			|||||||
    elements_string_fit_width(canvas, disp_str, 128 - 2);
 | 
					    elements_string_fit_width(canvas, disp_str, 128 - 2);
 | 
				
			||||||
    canvas_set_font(canvas, FontSecondary);
 | 
					    canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
    canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
 | 
					    canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(strlen(model->layout) == 0) {
 | 
				
			||||||
 | 
					        furi_string_set(disp_str, "(default)");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
 | 
					        furi_string_push_back(disp_str, '(');
 | 
				
			||||||
 | 
					        for(size_t i = 0; i < strlen(model->layout); i++)
 | 
				
			||||||
 | 
					            furi_string_push_back(disp_str, model->layout[i]);
 | 
				
			||||||
 | 
					        furi_string_push_back(disp_str, ')');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    elements_string_fit_width(canvas, disp_str, 128 - 2);
 | 
				
			||||||
 | 
					    canvas_draw_str(
 | 
				
			||||||
 | 
					        canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_string_reset(disp_str);
 | 
					    furi_string_reset(disp_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22);
 | 
					    canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
 | 
					    if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
 | 
				
			||||||
       (model->state.state == BadUsbStateNotConnected)) {
 | 
					       (model->state.state == BadUsbStateNotConnected)) {
 | 
				
			||||||
@ -38,23 +54,28 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
 | 
				
			|||||||
        elements_button_center(canvas, "Cancel");
 | 
					        elements_button_center(canvas, "Cancel");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if((model->state.state == BadUsbStateNotConnected) ||
 | 
				
			||||||
 | 
					       (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
 | 
				
			||||||
 | 
					        elements_button_left(canvas, "Config");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(model->state.state == BadUsbStateNotConnected) {
 | 
					    if(model->state.state == BadUsbStateNotConnected) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect");
 | 
					        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB");
 | 
					        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB");
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateWillRun) {
 | 
					    } else if(model->state.state == BadUsbStateWillRun) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run");
 | 
					        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect");
 | 
					        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateFileError) {
 | 
					    } else if(model->state.state == BadUsbStateFileError) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Error_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File");
 | 
					        canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR");
 | 
					        canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateScriptError) {
 | 
					    } else if(model->state.state == BadUsbStateScriptError) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Error_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
 | 
					        canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
 | 
				
			||||||
        canvas_set_font(canvas, FontSecondary);
 | 
					        canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
@ -64,49 +85,49 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
 | 
				
			|||||||
        furi_string_reset(disp_str);
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
 | 
					        canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateIdle) {
 | 
					    } else if(model->state.state == BadUsbStateIdle) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
 | 
				
			||||||
        canvas_set_font(canvas, FontBigNumbers);
 | 
					        canvas_set_font(canvas, FontBigNumbers);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0");
 | 
					        canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
 | 
				
			||||||
        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
 | 
					        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateRunning) {
 | 
					    } else if(model->state.state == BadUsbStateRunning) {
 | 
				
			||||||
        if(model->anim_frame == 0) {
 | 
					        if(model->anim_frame == 0) {
 | 
				
			||||||
            canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21);
 | 
					            canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21);
 | 
					            canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        canvas_set_font(canvas, FontBigNumbers);
 | 
					        canvas_set_font(canvas, FontBigNumbers);
 | 
				
			||||||
        furi_string_printf(
 | 
					        furi_string_printf(
 | 
				
			||||||
            disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
 | 
					            disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
 | 
				
			||||||
        canvas_draw_str_aligned(
 | 
					        canvas_draw_str_aligned(
 | 
				
			||||||
            canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
					            canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
				
			||||||
        furi_string_reset(disp_str);
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
 | 
					        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateDone) {
 | 
					    } else if(model->state.state == BadUsbStateDone) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21);
 | 
					        canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
 | 
				
			||||||
        canvas_set_font(canvas, FontBigNumbers);
 | 
					        canvas_set_font(canvas, FontBigNumbers);
 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100");
 | 
					        canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
 | 
				
			||||||
        furi_string_reset(disp_str);
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
 | 
					        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
 | 
				
			||||||
    } else if(model->state.state == BadUsbStateDelay) {
 | 
					    } else if(model->state.state == BadUsbStateDelay) {
 | 
				
			||||||
        if(model->anim_frame == 0) {
 | 
					        if(model->anim_frame == 0) {
 | 
				
			||||||
            canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21);
 | 
					            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21);
 | 
					            canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        canvas_set_font(canvas, FontBigNumbers);
 | 
					        canvas_set_font(canvas, FontBigNumbers);
 | 
				
			||||||
        furi_string_printf(
 | 
					        furi_string_printf(
 | 
				
			||||||
            disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
 | 
					            disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
 | 
				
			||||||
        canvas_draw_str_aligned(
 | 
					        canvas_draw_str_aligned(
 | 
				
			||||||
            canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
					            canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
				
			||||||
        furi_string_reset(disp_str);
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
        canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14);
 | 
					        canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
 | 
				
			||||||
        canvas_set_font(canvas, FontSecondary);
 | 
					        canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
        furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
 | 
					        furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
 | 
				
			||||||
        canvas_draw_str_aligned(
 | 
					        canvas_draw_str_aligned(
 | 
				
			||||||
            canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
					            canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
 | 
				
			||||||
        furi_string_reset(disp_str);
 | 
					        furi_string_reset(disp_str);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
 | 
					        canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_string_free(disp_str);
 | 
					    furi_string_free(disp_str);
 | 
				
			||||||
@ -118,10 +139,10 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) {
 | 
				
			|||||||
    bool consumed = false;
 | 
					    bool consumed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(event->type == InputTypeShort) {
 | 
					    if(event->type == InputTypeShort) {
 | 
				
			||||||
        if(event->key == InputKeyOk) {
 | 
					        if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
            furi_assert(bad_usb->callback);
 | 
					            furi_assert(bad_usb->callback);
 | 
				
			||||||
            bad_usb->callback(InputTypeShort, bad_usb->context);
 | 
					            bad_usb->callback(event->key, bad_usb->context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -151,7 +172,7 @@ View* bad_usb_get_view(BadUsb* bad_usb) {
 | 
				
			|||||||
    return bad_usb->view;
 | 
					    return bad_usb->view;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) {
 | 
					void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) {
 | 
				
			||||||
    furi_assert(bad_usb);
 | 
					    furi_assert(bad_usb);
 | 
				
			||||||
    furi_assert(callback);
 | 
					    furi_assert(callback);
 | 
				
			||||||
    with_view_model(
 | 
					    with_view_model(
 | 
				
			||||||
@ -174,6 +195,14 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) {
 | 
				
			|||||||
        true);
 | 
					        true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) {
 | 
				
			||||||
 | 
					    furi_assert(layout);
 | 
				
			||||||
 | 
					    with_view_model(
 | 
				
			||||||
 | 
					        bad_usb->view,
 | 
				
			||||||
 | 
					        BadUsbModel * model,
 | 
				
			||||||
 | 
					        { strlcpy(model->layout, layout, MAX_NAME_LEN); },
 | 
				
			||||||
 | 
					        true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
 | 
					void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
 | 
				
			||||||
    furi_assert(st);
 | 
					    furi_assert(st);
 | 
				
			||||||
    with_view_model(
 | 
					    with_view_model(
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
#include "../bad_usb_script.h"
 | 
					#include "../bad_usb_script.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct BadUsb BadUsb;
 | 
					typedef struct BadUsb BadUsb;
 | 
				
			||||||
typedef void (*BadUsbOkCallback)(InputType type, void* context);
 | 
					typedef void (*BadUsbButtonCallback)(InputKey key, void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BadUsb* bad_usb_alloc();
 | 
					BadUsb* bad_usb_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,8 +12,10 @@ void bad_usb_free(BadUsb* bad_usb);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
View* bad_usb_get_view(BadUsb* bad_usb);
 | 
					View* bad_usb_get_view(BadUsb* bad_usb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context);
 | 
					void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_set_file_name(BadUsb* bad_usb, const char* name);
 | 
					void bad_usb_set_file_name(BadUsb* bad_usb, const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bad_usb_set_layout(BadUsb* bad_usb, const char* layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st);
 | 
					void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st);
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() {
 | 
				
			|||||||
    GpioApp* app = malloc(sizeof(GpioApp));
 | 
					    GpioApp* app = malloc(sizeof(GpioApp));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app->gui = furi_record_open(RECORD_GUI);
 | 
					    app->gui = furi_record_open(RECORD_GUI);
 | 
				
			||||||
 | 
					    app->gpio_items = gpio_items_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app->view_dispatcher = view_dispatcher_alloc();
 | 
					    app->view_dispatcher = view_dispatcher_alloc();
 | 
				
			||||||
    app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
 | 
					    app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
 | 
				
			||||||
@ -47,7 +48,7 @@ GpioApp* gpio_app_alloc() {
 | 
				
			|||||||
        app->view_dispatcher,
 | 
					        app->view_dispatcher,
 | 
				
			||||||
        GpioAppViewVarItemList,
 | 
					        GpioAppViewVarItemList,
 | 
				
			||||||
        variable_item_list_get_view(app->var_item_list));
 | 
					        variable_item_list_get_view(app->var_item_list));
 | 
				
			||||||
    app->gpio_test = gpio_test_alloc();
 | 
					    app->gpio_test = gpio_test_alloc(app->gpio_items);
 | 
				
			||||||
    view_dispatcher_add_view(
 | 
					    view_dispatcher_add_view(
 | 
				
			||||||
        app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
 | 
					        app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,6 +92,7 @@ void gpio_app_free(GpioApp* app) {
 | 
				
			|||||||
    furi_record_close(RECORD_GUI);
 | 
					    furi_record_close(RECORD_GUI);
 | 
				
			||||||
    furi_record_close(RECORD_NOTIFICATION);
 | 
					    furi_record_close(RECORD_NOTIFICATION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gpio_items_free(app->gpio_items);
 | 
				
			||||||
    free(app);
 | 
					    free(app);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "gpio_app.h"
 | 
					#include "gpio_app.h"
 | 
				
			||||||
#include "gpio_item.h"
 | 
					#include "gpio_items.h"
 | 
				
			||||||
#include "scenes/gpio_scene.h"
 | 
					#include "scenes/gpio_scene.h"
 | 
				
			||||||
#include "gpio_custom_event.h"
 | 
					#include "gpio_custom_event.h"
 | 
				
			||||||
#include "usb_uart_bridge.h"
 | 
					#include "usb_uart_bridge.h"
 | 
				
			||||||
@ -28,6 +28,7 @@ struct GpioApp {
 | 
				
			|||||||
    VariableItem* var_item_flow;
 | 
					    VariableItem* var_item_flow;
 | 
				
			||||||
    GpioTest* gpio_test;
 | 
					    GpioTest* gpio_test;
 | 
				
			||||||
    GpioUsbUart* gpio_usb_uart;
 | 
					    GpioUsbUart* gpio_usb_uart;
 | 
				
			||||||
 | 
					    GPIOItems* gpio_items;
 | 
				
			||||||
    UsbUartBridge* usb_uart_bridge;
 | 
					    UsbUartBridge* usb_uart_bridge;
 | 
				
			||||||
    UsbUartConfig* usb_uart_cfg;
 | 
					    UsbUartConfig* usb_uart_cfg;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,51 +0,0 @@
 | 
				
			|||||||
#include "gpio_item.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <furi_hal_resources.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    const char* name;
 | 
					 | 
				
			||||||
    const GpioPin* pin;
 | 
					 | 
				
			||||||
} GpioItem;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
 | 
					 | 
				
			||||||
    {"1.2: PA7", &gpio_ext_pa7},
 | 
					 | 
				
			||||||
    {"1.3: PA6", &gpio_ext_pa6},
 | 
					 | 
				
			||||||
    {"1.4: PA4", &gpio_ext_pa4},
 | 
					 | 
				
			||||||
    {"1.5: PB3", &gpio_ext_pb3},
 | 
					 | 
				
			||||||
    {"1.6: PB2", &gpio_ext_pb2},
 | 
					 | 
				
			||||||
    {"1.7: PC3", &gpio_ext_pc3},
 | 
					 | 
				
			||||||
    {"2.7: PC1", &gpio_ext_pc1},
 | 
					 | 
				
			||||||
    {"2.8: PC0", &gpio_ext_pc0},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_configure_pin(uint8_t index, GpioMode mode) {
 | 
					 | 
				
			||||||
    furi_assert(index < GPIO_ITEM_COUNT);
 | 
					 | 
				
			||||||
    furi_hal_gpio_write(gpio_item[index].pin, false);
 | 
					 | 
				
			||||||
    furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_configure_all_pins(GpioMode mode) {
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
 | 
					 | 
				
			||||||
        gpio_item_configure_pin(i, mode);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_set_pin(uint8_t index, bool level) {
 | 
					 | 
				
			||||||
    furi_assert(index < GPIO_ITEM_COUNT);
 | 
					 | 
				
			||||||
    furi_hal_gpio_write(gpio_item[index].pin, level);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_set_all_pins(bool level) {
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
 | 
					 | 
				
			||||||
        gpio_item_set_pin(i, level);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const char* gpio_item_get_pin_name(uint8_t index) {
 | 
					 | 
				
			||||||
    furi_assert(index < GPIO_ITEM_COUNT + 1);
 | 
					 | 
				
			||||||
    if(index == GPIO_ITEM_COUNT) {
 | 
					 | 
				
			||||||
        return "ALL";
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        return gpio_item[index].name;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <furi_hal_gpio.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define GPIO_ITEM_COUNT 8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_configure_pin(uint8_t index, GpioMode mode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_configure_all_pins(GpioMode mode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_set_pin(uint8_t index, bool level);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void gpio_item_set_all_pins(bool level);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const char* gpio_item_get_pin_name(uint8_t index);
 | 
					 | 
				
			||||||
							
								
								
									
										69
									
								
								applications/main/gpio/gpio_items.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					#include "gpio_items.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi_hal_resources.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GPIOItems {
 | 
				
			||||||
 | 
					    GpioPinRecord* pins;
 | 
				
			||||||
 | 
					    size_t count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GPIOItems* gpio_items_alloc() {
 | 
				
			||||||
 | 
					    GPIOItems* items = malloc(sizeof(GPIOItems));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    items->count = 0;
 | 
				
			||||||
 | 
					    for(size_t i = 0; i < gpio_pins_count; i++) {
 | 
				
			||||||
 | 
					        if(!gpio_pins[i].debug) {
 | 
				
			||||||
 | 
					            items->count++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    items->pins = malloc(sizeof(GpioPinRecord) * items->count);
 | 
				
			||||||
 | 
					    for(size_t i = 0; i < items->count; i++) {
 | 
				
			||||||
 | 
					        if(!gpio_pins[i].debug) {
 | 
				
			||||||
 | 
					            items->pins[i].pin = gpio_pins[i].pin;
 | 
				
			||||||
 | 
					            items->pins[i].name = gpio_pins[i].name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return items;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_free(GPIOItems* items) {
 | 
				
			||||||
 | 
					    free(items->pins);
 | 
				
			||||||
 | 
					    free(items);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t gpio_items_get_count(GPIOItems* items) {
 | 
				
			||||||
 | 
					    return items->count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) {
 | 
				
			||||||
 | 
					    furi_assert(index < items->count);
 | 
				
			||||||
 | 
					    furi_hal_gpio_write(items->pins[index].pin, false);
 | 
				
			||||||
 | 
					    furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) {
 | 
				
			||||||
 | 
					    for(uint8_t i = 0; i < items->count; i++) {
 | 
				
			||||||
 | 
					        gpio_items_configure_pin(items, i, mode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) {
 | 
				
			||||||
 | 
					    furi_assert(index < items->count);
 | 
				
			||||||
 | 
					    furi_hal_gpio_write(items->pins[index].pin, level);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_set_all_pins(GPIOItems* items, bool level) {
 | 
				
			||||||
 | 
					    for(uint8_t i = 0; i < items->count; i++) {
 | 
				
			||||||
 | 
					        gpio_items_set_pin(items, i, level);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) {
 | 
				
			||||||
 | 
					    furi_assert(index < items->count + 1);
 | 
				
			||||||
 | 
					    if(index == items->count) {
 | 
				
			||||||
 | 
					        return "ALL";
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return items->pins[index].name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								applications/main/gpio/gpio_items.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi_hal_gpio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct GPIOItems GPIOItems;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GPIOItems* gpio_items_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_free(GPIOItems* items);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t gpio_items_get_count(GPIOItems* items);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gpio_items_set_all_pins(GPIOItems* items, bool level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
#include "../gpio_app_i.h"
 | 
					#include "../gpio_app_i.h"
 | 
				
			||||||
#include "furi_hal_power.h"
 | 
					#include <furi_hal_power.h>
 | 
				
			||||||
#include "furi_hal_usb.h"
 | 
					#include <furi_hal_usb.h>
 | 
				
			||||||
#include <dolphin/dolphin.h>
 | 
					#include <dolphin/dolphin.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum GpioItem {
 | 
					enum GpioItem {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void gpio_scene_test_on_enter(void* context) {
 | 
					void gpio_scene_test_on_enter(void* context) {
 | 
				
			||||||
 | 
					    furi_assert(context);
 | 
				
			||||||
    GpioApp* app = context;
 | 
					    GpioApp* app = context;
 | 
				
			||||||
    gpio_item_configure_all_pins(GpioModeOutputPushPull);
 | 
					    gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull);
 | 
				
			||||||
    gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
 | 
					    gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
 | 
				
			||||||
    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
 | 
					    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void gpio_scene_test_on_exit(void* context) {
 | 
					void gpio_scene_test_on_exit(void* context) {
 | 
				
			||||||
    UNUSED(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
    gpio_item_configure_all_pins(GpioModeAnalog);
 | 
					    GpioApp* app = context;
 | 
				
			||||||
 | 
					    gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
#include "../usb_uart_bridge.h"
 | 
					#include "../usb_uart_bridge.h"
 | 
				
			||||||
#include "../gpio_app_i.h"
 | 
					#include "../gpio_app_i.h"
 | 
				
			||||||
#include "furi_hal.h"
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    UsbUartLineIndexVcp,
 | 
					    UsbUartLineIndexVcp,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
#include "usb_uart_bridge.h"
 | 
					#include "usb_uart_bridge.h"
 | 
				
			||||||
#include "furi_hal.h"
 | 
					 | 
				
			||||||
#include <furi_hal_usb_cdc.h>
 | 
					 | 
				
			||||||
#include "usb_cdc.h"
 | 
					#include "usb_cdc.h"
 | 
				
			||||||
#include "cli/cli_vcp.h"
 | 
					#include <cli/cli_vcp.h>
 | 
				
			||||||
 | 
					#include <cli/cli.h>
 | 
				
			||||||
#include <toolbox/api_lock.h>
 | 
					#include <toolbox/api_lock.h>
 | 
				
			||||||
#include "cli/cli.h"
 | 
					#include <furi_hal.h>
 | 
				
			||||||
 | 
					#include <furi_hal_usb_cdc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define USB_CDC_PKT_LEN CDC_DATA_SZ
 | 
					#define USB_CDC_PKT_LEN CDC_DATA_SZ
 | 
				
			||||||
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
 | 
					#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
#include "gpio_test.h"
 | 
					#include "gpio_test.h"
 | 
				
			||||||
#include "../gpio_item.h"
 | 
					#include "../gpio_items.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,6 +11,7 @@ struct GpioTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    uint8_t pin_idx;
 | 
					    uint8_t pin_idx;
 | 
				
			||||||
 | 
					    GPIOItems* gpio_items;
 | 
				
			||||||
} GpioTestModel;
 | 
					} GpioTestModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool gpio_test_process_left(GpioTest* gpio_test);
 | 
					static bool gpio_test_process_left(GpioTest* gpio_test);
 | 
				
			||||||
@ -25,7 +26,12 @@ static void gpio_test_draw_callback(Canvas* canvas, void* _model) {
 | 
				
			|||||||
    elements_multiline_text_aligned(
 | 
					    elements_multiline_text_aligned(
 | 
				
			||||||
        canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin");
 | 
					        canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin");
 | 
				
			||||||
    elements_multiline_text_aligned(
 | 
					    elements_multiline_text_aligned(
 | 
				
			||||||
        canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx));
 | 
					        canvas,
 | 
				
			||||||
 | 
					        64,
 | 
				
			||||||
 | 
					        32,
 | 
				
			||||||
 | 
					        AlignCenter,
 | 
				
			||||||
 | 
					        AlignTop,
 | 
				
			||||||
 | 
					        gpio_items_get_pin_name(model->gpio_items, model->pin_idx));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool gpio_test_input_callback(InputEvent* event, void* context) {
 | 
					static bool gpio_test_input_callback(InputEvent* event, void* context) {
 | 
				
			||||||
@ -64,7 +70,7 @@ static bool gpio_test_process_right(GpioTest* gpio_test) {
 | 
				
			|||||||
        gpio_test->view,
 | 
					        gpio_test->view,
 | 
				
			||||||
        GpioTestModel * model,
 | 
					        GpioTestModel * model,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if(model->pin_idx < GPIO_ITEM_COUNT) {
 | 
					            if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
 | 
				
			||||||
                model->pin_idx++;
 | 
					                model->pin_idx++;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -80,17 +86,17 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
 | 
				
			|||||||
        GpioTestModel * model,
 | 
					        GpioTestModel * model,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if(event->type == InputTypePress) {
 | 
					            if(event->type == InputTypePress) {
 | 
				
			||||||
                if(model->pin_idx < GPIO_ITEM_COUNT) {
 | 
					                if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
 | 
				
			||||||
                    gpio_item_set_pin(model->pin_idx, true);
 | 
					                    gpio_items_set_pin(model->gpio_items, model->pin_idx, true);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    gpio_item_set_all_pins(true);
 | 
					                    gpio_items_set_all_pins(model->gpio_items, true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                consumed = true;
 | 
					                consumed = true;
 | 
				
			||||||
            } else if(event->type == InputTypeRelease) {
 | 
					            } else if(event->type == InputTypeRelease) {
 | 
				
			||||||
                if(model->pin_idx < GPIO_ITEM_COUNT) {
 | 
					                if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
 | 
				
			||||||
                    gpio_item_set_pin(model->pin_idx, false);
 | 
					                    gpio_items_set_pin(model->gpio_items, model->pin_idx, false);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    gpio_item_set_all_pins(false);
 | 
					                    gpio_items_set_all_pins(model->gpio_items, false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                consumed = true;
 | 
					                consumed = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -101,11 +107,15 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
 | 
				
			|||||||
    return consumed;
 | 
					    return consumed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GpioTest* gpio_test_alloc() {
 | 
					GpioTest* gpio_test_alloc(GPIOItems* gpio_items) {
 | 
				
			||||||
    GpioTest* gpio_test = malloc(sizeof(GpioTest));
 | 
					    GpioTest* gpio_test = malloc(sizeof(GpioTest));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gpio_test->view = view_alloc();
 | 
					    gpio_test->view = view_alloc();
 | 
				
			||||||
    view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
 | 
					    view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with_view_model(
 | 
				
			||||||
 | 
					        gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_set_context(gpio_test->view, gpio_test);
 | 
					    view_set_context(gpio_test->view, gpio_test);
 | 
				
			||||||
    view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);
 | 
					    view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);
 | 
				
			||||||
    view_set_input_callback(gpio_test->view, gpio_test_input_callback);
 | 
					    view_set_input_callback(gpio_test->view, gpio_test_input_callback);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,13 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../gpio_items.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <gui/view.h>
 | 
					#include <gui/view.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct GpioTest GpioTest;
 | 
					typedef struct GpioTest GpioTest;
 | 
				
			||||||
typedef void (*GpioTestOkCallback)(InputType type, void* context);
 | 
					typedef void (*GpioTestOkCallback)(InputType type, void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GpioTest* gpio_test_alloc();
 | 
					GpioTest* gpio_test_alloc(GPIOItems* gpio_items);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void gpio_test_free(GpioTest* gpio_test);
 | 
					void gpio_test_free(GpioTest* gpio_test);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
#include "../usb_uart_bridge.h"
 | 
					#include "../usb_uart_bridge.h"
 | 
				
			||||||
#include "../gpio_app_i.h"
 | 
					#include "../gpio_app_i.h"
 | 
				
			||||||
#include "furi_hal.h"
 | 
					#include <furi_hal.h>
 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct GpioUsbUart {
 | 
					struct GpioUsbUart {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="ibutton",
 | 
					    appid="ibutton",
 | 
				
			||||||
    name="iButton",
 | 
					    name="iButton",
 | 
				
			||||||
    apptype=FlipperAppType.APP,
 | 
					    apptype=FlipperAppType.APP,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="ibutton_app",
 | 
					    entry_point="ibutton_app",
 | 
				
			||||||
    cdefines=["APP_IBUTTON"],
 | 
					    cdefines=["APP_IBUTTON"],
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
 | 
				
			|||||||
@ -271,7 +271,7 @@ void onewire_cli_print_usage() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void onewire_cli_search(Cli* cli) {
 | 
					static void onewire_cli_search(Cli* cli) {
 | 
				
			||||||
    UNUSED(cli);
 | 
					    UNUSED(cli);
 | 
				
			||||||
    OneWireHost* onewire = onewire_host_alloc();
 | 
					    OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
 | 
				
			||||||
    uint8_t address[8];
 | 
					    uint8_t address[8];
 | 
				
			||||||
    bool done = false;
 | 
					    bool done = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ App(
 | 
				
			|||||||
    name="Infrared",
 | 
					    name="Infrared",
 | 
				
			||||||
    apptype=FlipperAppType.APP,
 | 
					    apptype=FlipperAppType.APP,
 | 
				
			||||||
    entry_point="infrared_app",
 | 
					    entry_point="infrared_app",
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    cdefines=["APP_INFRARED"],
 | 
					    cdefines=["APP_INFRARED"],
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
        "gui",
 | 
					        "gui",
 | 
				
			||||||
 | 
				
			|||||||
@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) {
 | 
				
			|||||||
    printf("\tir universal <remote_name> <signal_name>\r\n");
 | 
					    printf("\tir universal <remote_name> <signal_name>\r\n");
 | 
				
			||||||
    printf("\tir universal list <remote_name>\r\n");
 | 
					    printf("\tir universal list <remote_name>\r\n");
 | 
				
			||||||
    // TODO: Do not hardcode universal remote names
 | 
					    // TODO: Do not hardcode universal remote names
 | 
				
			||||||
    printf("\tAvailable universal remotes: tv audio ac\r\n");
 | 
					    printf("\tAvailable universal remotes: tv audio ac projector\r\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
 | 
					static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ ADD_SCENE(infrared, universal, Universal)
 | 
				
			|||||||
ADD_SCENE(infrared, universal_tv, UniversalTV)
 | 
					ADD_SCENE(infrared, universal_tv, UniversalTV)
 | 
				
			||||||
ADD_SCENE(infrared, universal_ac, UniversalAC)
 | 
					ADD_SCENE(infrared, universal_ac, UniversalAC)
 | 
				
			||||||
ADD_SCENE(infrared, universal_audio, UniversalAudio)
 | 
					ADD_SCENE(infrared, universal_audio, UniversalAudio)
 | 
				
			||||||
 | 
					ADD_SCENE(infrared, universal_projector, UniversalProjector)
 | 
				
			||||||
ADD_SCENE(infrared, debug, Debug)
 | 
					ADD_SCENE(infrared, debug, Debug)
 | 
				
			||||||
ADD_SCENE(infrared, error_databases, ErrorDatabases)
 | 
					ADD_SCENE(infrared, error_databases, ErrorDatabases)
 | 
				
			||||||
ADD_SCENE(infrared, rpc, Rpc)
 | 
					ADD_SCENE(infrared, rpc, Rpc)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
#include "../infrared_i.h"
 | 
					#include "../infrared_i.h"
 | 
				
			||||||
#include "gui/canvas.h"
 | 
					#include <gui/canvas.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    InfraredRpcStateIdle,
 | 
					    InfraredRpcStateIdle,
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ typedef enum {
 | 
				
			|||||||
    SubmenuIndexUniversalTV,
 | 
					    SubmenuIndexUniversalTV,
 | 
				
			||||||
    SubmenuIndexUniversalAC,
 | 
					    SubmenuIndexUniversalAC,
 | 
				
			||||||
    SubmenuIndexUniversalAudio,
 | 
					    SubmenuIndexUniversalAudio,
 | 
				
			||||||
 | 
					    SubmenuIndexUniversalProjector,
 | 
				
			||||||
} SubmenuIndex;
 | 
					} SubmenuIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
 | 
					static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
 | 
				
			||||||
@ -27,13 +28,20 @@ void infrared_scene_universal_on_enter(void* context) {
 | 
				
			|||||||
        SubmenuIndexUniversalAudio,
 | 
					        SubmenuIndexUniversalAudio,
 | 
				
			||||||
        infrared_scene_universal_submenu_callback,
 | 
					        infrared_scene_universal_submenu_callback,
 | 
				
			||||||
        context);
 | 
					        context);
 | 
				
			||||||
 | 
					    submenu_add_item(
 | 
				
			||||||
 | 
					        submenu,
 | 
				
			||||||
 | 
					        "Projectors",
 | 
				
			||||||
 | 
					        SubmenuIndexUniversalProjector,
 | 
				
			||||||
 | 
					        infrared_scene_universal_submenu_callback,
 | 
				
			||||||
 | 
					        context);
 | 
				
			||||||
    submenu_add_item(
 | 
					    submenu_add_item(
 | 
				
			||||||
        submenu,
 | 
					        submenu,
 | 
				
			||||||
        "Air Conditioners",
 | 
					        "Air Conditioners",
 | 
				
			||||||
        SubmenuIndexUniversalAC,
 | 
					        SubmenuIndexUniversalAC,
 | 
				
			||||||
        infrared_scene_universal_submenu_callback,
 | 
					        infrared_scene_universal_submenu_callback,
 | 
				
			||||||
        context);
 | 
					        context);
 | 
				
			||||||
    submenu_set_selected_item(submenu, 0);
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
 | 
					        submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
 | 
					    view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -53,7 +61,11 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
        } else if(event.event == SubmenuIndexUniversalAudio) {
 | 
					        } else if(event.event == SubmenuIndexUniversalAudio) {
 | 
				
			||||||
            scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio);
 | 
					            scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
 | 
					        } else if(event.event == SubmenuIndexUniversalProjector) {
 | 
				
			||||||
 | 
					            scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);
 | 
				
			||||||
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return consumed;
 | 
					    return consumed;
 | 
				
			||||||
@ -62,4 +74,4 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
void infrared_scene_universal_on_exit(void* context) {
 | 
					void infrared_scene_universal_on_exit(void* context) {
 | 
				
			||||||
    Infrared* infrared = context;
 | 
					    Infrared* infrared = context;
 | 
				
			||||||
    submenu_reset(infrared->submenu);
 | 
					    submenu_reset(infrared->submenu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -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/projector.ir"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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,
 | 
				
			||||||
 | 
					        66,
 | 
				
			||||||
 | 
					        &I_Vol_up_25x27,
 | 
				
			||||||
 | 
					        &I_Vol_up_hvr_25x27,
 | 
				
			||||||
 | 
					        infrared_scene_universal_common_item_callback,
 | 
				
			||||||
 | 
					        context);
 | 
				
			||||||
 | 
					    infrared_brute_force_add_record(brute_force, i++, "Vol_up");
 | 
				
			||||||
 | 
					    button_panel_add_item(
 | 
				
			||||||
 | 
					        button_panel,
 | 
				
			||||||
 | 
					        i,
 | 
				
			||||||
 | 
					        1,
 | 
				
			||||||
 | 
					        1,
 | 
				
			||||||
 | 
					        36,
 | 
				
			||||||
 | 
					        66,
 | 
				
			||||||
 | 
					        &I_Vol_down_25x27,
 | 
				
			||||||
 | 
					        &I_Vol_down_hvr_25x27,
 | 
				
			||||||
 | 
					        infrared_scene_universal_common_item_callback,
 | 
				
			||||||
 | 
					        context);
 | 
				
			||||||
 | 
					    infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    button_panel_add_label(button_panel, 2, 11, FontPrimary, "Proj. remote");
 | 
				
			||||||
 | 
					    button_panel_add_label(button_panel, 17, 62, 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
#include "infrared_debug_view.h"
 | 
					#include "infrared_debug_view.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <gui/canvas.h>
 | 
					#include <gui/canvas.h>
 | 
				
			||||||
#include <gui/elements.h>
 | 
					#include <gui/elements.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define INFRARED_DEBUG_TEXT_LENGTH 64
 | 
					#define INFRARED_DEBUG_TEXT_LENGTH 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct InfraredDebugView {
 | 
					struct InfraredDebugView {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,15 @@
 | 
				
			|||||||
#include <core/check.h>
 | 
					 | 
				
			||||||
#include "furi_hal_resources.h"
 | 
					 | 
				
			||||||
#include "assets_icons.h"
 | 
					 | 
				
			||||||
#include "gui/canvas.h"
 | 
					 | 
				
			||||||
#include "gui/view.h"
 | 
					 | 
				
			||||||
#include "input/input.h"
 | 
					 | 
				
			||||||
#include <gui/elements.h>
 | 
					 | 
				
			||||||
#include <furi.h>
 | 
					 | 
				
			||||||
#include "infrared_progress_view.h"
 | 
					#include "infrared_progress_view.h"
 | 
				
			||||||
#include "gui/modules/button_panel.h"
 | 
					
 | 
				
			||||||
 | 
					#include <assets_icons.h>
 | 
				
			||||||
 | 
					#include <gui/canvas.h>
 | 
				
			||||||
 | 
					#include <gui/view.h>
 | 
				
			||||||
 | 
					#include <gui/elements.h>
 | 
				
			||||||
 | 
					#include <gui/modules/button_panel.h>
 | 
				
			||||||
 | 
					#include <input/input.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					#include <furi_hal_resources.h>
 | 
				
			||||||
 | 
					#include <core/check.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct InfraredProgressView {
 | 
					struct InfraredProgressView {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="lfrfid",
 | 
					    appid="lfrfid",
 | 
				
			||||||
    name="125 kHz RFID",
 | 
					    name="125 kHz RFID",
 | 
				
			||||||
    apptype=FlipperAppType.APP,
 | 
					    apptype=FlipperAppType.APP,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="lfrfid_app",
 | 
					    entry_point="lfrfid_app",
 | 
				
			||||||
    cdefines=["APP_LF_RFID"],
 | 
					    cdefines=["APP_LF_RFID"],
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
 | 
				
			|||||||
@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        if(event.event == SubmenuIndexRead) {
 | 
					        if(event.event == SubmenuIndexRead) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
 | 
					            scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
 | 
				
			||||||
            DOLPHIN_DEED(DolphinDeedRfidRead);
 | 
					            DOLPHIN_DEED(DolphinDeedRfidRead);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexSaved) {
 | 
					        } else if(event.event == SubmenuIndexSaved) {
 | 
				
			||||||
 | 
					            // Like in the other apps, explicitly save the scene state
 | 
				
			||||||
 | 
					            // in each branch in case the user cancels loading a file.
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved);
 | 
				
			||||||
            furi_string_set(app->file_path, LFRFID_APP_FOLDER);
 | 
					            furi_string_set(app->file_path, LFRFID_APP_FOLDER);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
 | 
					            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexAddManually) {
 | 
					        } else if(event.event == SubmenuIndexAddManually) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
 | 
					            scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexExtraActions) {
 | 
					        } else if(event.event == SubmenuIndexExtraActions) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions);
 | 
				
			||||||
            scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
 | 
					            scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return consumed;
 | 
					    return consumed;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="nfc",
 | 
					    appid="nfc",
 | 
				
			||||||
    name="NFC",
 | 
					    name="NFC",
 | 
				
			||||||
    apptype=FlipperAppType.APP,
 | 
					    apptype=FlipperAppType.APP,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="nfc_app",
 | 
					    entry_point="nfc_app",
 | 
				
			||||||
    cdefines=["APP_NFC"],
 | 
					    cdefines=["APP_NFC"],
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
#include "nfc_i.h"
 | 
					#include "nfc_i.h"
 | 
				
			||||||
#include "furi_hal_nfc.h"
 | 
					#include <furi_hal_nfc.h>
 | 
				
			||||||
#include <dolphin/dolphin.h>
 | 
					#include <dolphin/dolphin.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_custom_event_callback(void* context, uint32_t event) {
 | 
					bool nfc_custom_event_callback(void* context, uint32_t event) {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ ADD_SCENE(nfc, saved_menu, SavedMenu)
 | 
				
			|||||||
ADD_SCENE(nfc, extra_actions, ExtraActions)
 | 
					ADD_SCENE(nfc, extra_actions, ExtraActions)
 | 
				
			||||||
ADD_SCENE(nfc, set_type, SetType)
 | 
					ADD_SCENE(nfc, set_type, SetType)
 | 
				
			||||||
ADD_SCENE(nfc, set_sak, SetSak)
 | 
					ADD_SCENE(nfc, set_sak, SetSak)
 | 
				
			||||||
ADD_SCENE(nfc, set_atqa, SetAtqua)
 | 
					ADD_SCENE(nfc, set_atqa, SetAtqa)
 | 
				
			||||||
ADD_SCENE(nfc, set_uid, SetUid)
 | 
					ADD_SCENE(nfc, set_uid, SetUid)
 | 
				
			||||||
ADD_SCENE(nfc, generate_info, GenerateInfo)
 | 
					ADD_SCENE(nfc, generate_info, GenerateInfo)
 | 
				
			||||||
ADD_SCENE(nfc, read_card_success, ReadCardSuccess)
 | 
					ADD_SCENE(nfc, read_card_success, ReadCardSuccess)
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
                    nfc->scene_manager, NfcSceneMfClassicKeys);
 | 
					                    nfc->scene_manager, NfcSceneMfClassicKeys);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                consumed = scene_manager_search_and_switch_to_previous_scene(
 | 
					                consumed = scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
                    nfc->scene_manager, NfcSceneStart);
 | 
					                    nfc->scene_manager, NfcSceneFileSelect);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,8 @@ void nfc_scene_extra_actions_on_enter(void* context) {
 | 
				
			|||||||
        SubmenuIndexMfUltralightUnlock,
 | 
					        SubmenuIndexMfUltralightUnlock,
 | 
				
			||||||
        nfc_scene_extra_actions_submenu_callback,
 | 
					        nfc_scene_extra_actions_submenu_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
 | 
					        submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions));
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ void nfc_scene_set_atqa_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    ByteInput* byte_input = nfc->byte_input;
 | 
					    ByteInput* byte_input = nfc->byte_input;
 | 
				
			||||||
    byte_input_set_header_text(byte_input, "Enter atqa in hex");
 | 
					    byte_input_set_header_text(byte_input, "Enter ATQA in hex");
 | 
				
			||||||
    byte_input_set_result_callback(
 | 
					    byte_input_set_result_callback(
 | 
				
			||||||
        byte_input,
 | 
					        byte_input,
 | 
				
			||||||
        nfc_scene_set_atqa_byte_input_callback,
 | 
					        nfc_scene_set_atqa_byte_input_callback,
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@ bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        if(event.event == NfcCustomEventByteInputDone) {
 | 
					        if(event.event == NfcCustomEventByteInputDone) {
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ void nfc_scene_set_uid_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    ByteInput* byte_input = nfc->byte_input;
 | 
					    ByteInput* byte_input = nfc->byte_input;
 | 
				
			||||||
    byte_input_set_header_text(byte_input, "Enter uid in hex");
 | 
					    byte_input_set_header_text(byte_input, "Enter UID in hex");
 | 
				
			||||||
    nfc->dev_edit_data = nfc->dev->dev_data.nfc_data;
 | 
					    nfc->dev_edit_data = nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    byte_input_set_result_callback(
 | 
					    byte_input_set_result_callback(
 | 
				
			||||||
        byte_input,
 | 
					        byte_input,
 | 
				
			||||||
 | 
				
			|||||||
@ -48,11 +48,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        if(event.event == SubmenuIndexRead) {
 | 
					        if(event.event == SubmenuIndexRead) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
 | 
				
			||||||
            nfc->dev->dev_data.read_mode = NfcReadModeAuto;
 | 
					            nfc->dev->dev_data.read_mode = NfcReadModeAuto;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
 | 
				
			||||||
            DOLPHIN_DEED(DolphinDeedNfcRead);
 | 
					            DOLPHIN_DEED(DolphinDeedNfcRead);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexDetectReader) {
 | 
					        } else if(event.event == SubmenuIndexDetectReader) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader);
 | 
				
			||||||
            bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
 | 
					            bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
 | 
				
			||||||
            if(sd_exist) {
 | 
					            if(sd_exist) {
 | 
				
			||||||
                nfc_device_data_clear(&nfc->dev->dev_data);
 | 
					                nfc_device_data_clear(&nfc->dev->dev_data);
 | 
				
			||||||
@ -63,19 +66,27 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexSaved) {
 | 
					        } else if(event.event == SubmenuIndexSaved) {
 | 
				
			||||||
 | 
					            // Save the scene state explicitly in each branch, so that
 | 
				
			||||||
 | 
					            // if the user cancels loading a file, the Saved menu item
 | 
				
			||||||
 | 
					            // is properly reselected.
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexExtraAction) {
 | 
					        } else if(event.event == SubmenuIndexExtraAction) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexAddManually) {
 | 
					        } else if(event.event == SubmenuIndexAddManually) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
 | 
					                nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexDebug) {
 | 
					        } else if(event.event == SubmenuIndexDebug) {
 | 
				
			||||||
 | 
					            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return consumed;
 | 
					    return consumed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="subghz",
 | 
					    appid="subghz",
 | 
				
			||||||
    name="Sub-GHz",
 | 
					    name="Sub-GHz",
 | 
				
			||||||
    apptype=FlipperAppType.APP,
 | 
					    apptype=FlipperAppType.APP,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="subghz_app",
 | 
					    entry_point="subghz_app",
 | 
				
			||||||
    cdefines=["APP_SUBGHZ"],
 | 
					    cdefines=["APP_SUBGHZ"],
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
@ -11,7 +12,7 @@ App(
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    provides=["subghz_start"],
 | 
					    provides=["subghz_start"],
 | 
				
			||||||
    icon="A_Sub1ghz_14",
 | 
					    icon="A_Sub1ghz_14",
 | 
				
			||||||
    stack_size=2 * 1024,
 | 
					    stack_size=3 * 1024,
 | 
				
			||||||
    order=10,
 | 
					    order=10,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,10 +28,8 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
 | 
				
			|||||||
        if(event.event == SubGhzCustomEventSceneDeleteSuccess) {
 | 
					        if(event.event == SubGhzCustomEventSceneDeleteSuccess) {
 | 
				
			||||||
            if(scene_manager_search_and_switch_to_previous_scene(
 | 
					            if(scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
                   subghz->scene_manager, SubGhzSceneReadRAW)) {
 | 
					                   subghz->scene_manager, SubGhzSceneReadRAW)) {
 | 
				
			||||||
                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
 | 
					 | 
				
			||||||
            } else if(scene_manager_search_and_switch_to_previous_scene(
 | 
					            } else if(scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
                          subghz->scene_manager, SubGhzSceneSaved)) {
 | 
					                          subghz->scene_manager, SubGhzSceneSaved)) {
 | 
				
			||||||
                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                scene_manager_search_and_switch_to_previous_scene(
 | 
					                scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
                    subghz->scene_manager, SubGhzSceneStart);
 | 
					                    subghz->scene_manager, SubGhzSceneStart);
 | 
				
			||||||
 | 
				
			|||||||
@ -411,5 +411,5 @@ void subghz_scene_read_raw_on_exit(void* context) {
 | 
				
			|||||||
    notification_message(subghz->notifications, &sequence_reset_rgb);
 | 
					    notification_message(subghz->notifications, &sequence_reset_rgb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //filter restoration
 | 
					    //filter restoration
 | 
				
			||||||
    subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
 | 
					    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
#include "../subghz_i.h"
 | 
					#include "../subghz_i.h"
 | 
				
			||||||
#include "../views/receiver.h"
 | 
					#include "../views/receiver.h"
 | 
				
			||||||
#include <dolphin/dolphin.h>
 | 
					#include <dolphin/dolphin.h>
 | 
				
			||||||
 | 
					#include <lib/subghz/protocols/bin_raw.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const NotificationSequence subghs_sequence_rx = {
 | 
					static const NotificationSequence subghs_sequence_rx = {
 | 
				
			||||||
    &message_green_255,
 | 
					    &message_green_255,
 | 
				
			||||||
@ -143,6 +144,11 @@ void subghz_scene_receiver_on_enter(void* context) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
 | 
					    subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //to use a universal decoder, we are looking for a link to it
 | 
				
			||||||
 | 
					    subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
 | 
				
			||||||
 | 
					        subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME);
 | 
				
			||||||
 | 
					    furi_assert(subghz->txrx->decoder_result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
 | 
					    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -208,6 +214,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            subghz_hopper_update(subghz);
 | 
					            subghz_hopper_update(subghz);
 | 
				
			||||||
            subghz_scene_receiver_update_statusbar(subghz);
 | 
					            subghz_scene_receiver_update_statusbar(subghz);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //get RSSI
 | 
				
			||||||
 | 
					        float rssi = furi_hal_subghz_get_rssi();
 | 
				
			||||||
 | 
					        subghz_receiver_rssi(subghz->subghz_receiver, rssi);
 | 
				
			||||||
 | 
					        subghz_protocol_decoder_bin_raw_data_input_rssi(
 | 
				
			||||||
 | 
					            (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch(subghz->state_notifications) {
 | 
					        switch(subghz->state_notifications) {
 | 
				
			||||||
        case SubGhzNotificationStateRx:
 | 
					        case SubGhzNotificationStateRx:
 | 
				
			||||||
            notification_message(subghz->notifications, &sequence_blink_cyan_10);
 | 
					            notification_message(subghz->notifications, &sequence_blink_cyan_10);
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ enum SubGhzSettingIndex {
 | 
				
			|||||||
    SubGhzSettingIndexFrequency,
 | 
					    SubGhzSettingIndexFrequency,
 | 
				
			||||||
    SubGhzSettingIndexHopping,
 | 
					    SubGhzSettingIndexHopping,
 | 
				
			||||||
    SubGhzSettingIndexModulation,
 | 
					    SubGhzSettingIndexModulation,
 | 
				
			||||||
 | 
					    SubGhzSettingIndexBinRAW,
 | 
				
			||||||
    SubGhzSettingIndexSound,
 | 
					    SubGhzSettingIndexSound,
 | 
				
			||||||
    SubGhzSettingIndexLock,
 | 
					    SubGhzSettingIndexLock,
 | 
				
			||||||
    SubGhzSettingIndexRAWThesholdRSSI,
 | 
					    SubGhzSettingIndexRAWThesholdRSSI,
 | 
				
			||||||
@ -58,6 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = {
 | 
				
			|||||||
    SubGhzSpeakerStateShutdown,
 | 
					    SubGhzSpeakerStateShutdown,
 | 
				
			||||||
    SubGhzSpeakerStateEnable,
 | 
					    SubGhzSpeakerStateEnable,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					#define BIN_RAW_COUNT 2
 | 
				
			||||||
 | 
					const char* const bin_raw_text[BIN_RAW_COUNT] = {
 | 
				
			||||||
 | 
					    "OFF",
 | 
				
			||||||
 | 
					    "ON",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
 | 
				
			||||||
 | 
					    SubGhzProtocolFlag_Decodable,
 | 
				
			||||||
 | 
					    SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
 | 
					uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
@ -186,6 +196,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
 | 
				
			|||||||
    subghz->txrx->speaker_state = speaker_value[index];
 | 
					    subghz->txrx->speaker_state = speaker_value[index];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
 | 
				
			||||||
 | 
					    SubGhz* subghz = variable_item_get_context(item);
 | 
				
			||||||
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    variable_item_set_current_value_text(item, bin_raw_text[index]);
 | 
				
			||||||
 | 
					    subghz->txrx->filter = bin_raw_value[index];
 | 
				
			||||||
 | 
					    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
 | 
					static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
 | 
				
			||||||
    SubGhz* subghz = variable_item_get_context(item);
 | 
					    SubGhz* subghz = variable_item_get_context(item);
 | 
				
			||||||
    uint8_t index = variable_item_get_current_value_index(item);
 | 
					    uint8_t index = variable_item_get_current_value_index(item);
 | 
				
			||||||
@ -254,6 +273,19 @@ void subghz_scene_receiver_config_on_enter(void* context) {
 | 
				
			|||||||
    variable_item_set_current_value_text(
 | 
					    variable_item_set_current_value_text(
 | 
				
			||||||
        item, subghz_setting_get_preset_name(subghz->setting, value_index));
 | 
					        item, subghz_setting_get_preset_name(subghz->setting, value_index));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
 | 
				
			||||||
 | 
					       SubGhzCustomEventManagerSet) {
 | 
				
			||||||
 | 
					        item = variable_item_list_add(
 | 
				
			||||||
 | 
					            subghz->variable_item_list,
 | 
				
			||||||
 | 
					            "Bin_RAW:",
 | 
				
			||||||
 | 
					            BIN_RAW_COUNT,
 | 
				
			||||||
 | 
					            subghz_scene_receiver_config_set_bin_raw,
 | 
				
			||||||
 | 
					            subghz);
 | 
				
			||||||
 | 
					        value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT);
 | 
				
			||||||
 | 
					        variable_item_set_current_value_index(item, value_index);
 | 
				
			||||||
 | 
					        variable_item_set_current_value_text(item, bin_raw_text[value_index]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    item = variable_item_list_add(
 | 
					    item = variable_item_list_add(
 | 
				
			||||||
        subghz->variable_item_list,
 | 
					        subghz->variable_item_list,
 | 
				
			||||||
        "Sound:",
 | 
					        "Sound:",
 | 
				
			||||||
 | 
				
			|||||||
@ -129,6 +129,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
 | 
				
			|||||||
                       subghz_history_get_raw_data(
 | 
					                       subghz_history_get_raw_data(
 | 
				
			||||||
                           subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
 | 
					                           subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
 | 
				
			||||||
                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
 | 
					                    scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
 | 
				
			||||||
 | 
					                    if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
 | 
				
			||||||
 | 
					                        subghz_tx_stop(subghz);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
 | 
				
			||||||
 | 
					                        subghz_begin(
 | 
				
			||||||
 | 
					                            subghz,
 | 
				
			||||||
 | 
					                            subghz_setting_get_preset_data_by_name(
 | 
				
			||||||
 | 
					                                subghz->setting,
 | 
				
			||||||
 | 
					                                furi_string_get_cstr(subghz->txrx->preset->name)));
 | 
				
			||||||
 | 
					                        subghz_rx(subghz, subghz->txrx->preset->frequency);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
 | 
				
			||||||
 | 
					                        subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    subghz->state_notifications = SubGhzNotificationStateRx;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    subghz->state_notifications = SubGhzNotificationStateTx;
 | 
					                    subghz->state_notifications = SubGhzNotificationStateTx;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -187,12 +187,15 @@ SubGhz* subghz_alloc() {
 | 
				
			|||||||
    subghz->txrx->environment = subghz_environment_alloc();
 | 
					    subghz->txrx->environment = subghz_environment_alloc();
 | 
				
			||||||
    subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
					    subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
				
			||||||
        subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
					        subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
				
			||||||
 | 
					    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
 | 
				
			||||||
 | 
					        subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
 | 
				
			||||||
    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
					    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
				
			||||||
        subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
					        subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
				
			||||||
    subghz_environment_set_protocol_registry(
 | 
					    subghz_environment_set_protocol_registry(
 | 
				
			||||||
        subghz->txrx->environment, (void*)&subghz_protocol_registry);
 | 
					        subghz->txrx->environment, (void*)&subghz_protocol_registry);
 | 
				
			||||||
    subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
 | 
					    subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
 | 
				
			||||||
    subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
 | 
					    subghz->txrx->filter = SubGhzProtocolFlag_Decodable;
 | 
				
			||||||
 | 
					    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subghz_worker_set_overrun_callback(
 | 
					    subghz_worker_set_overrun_callback(
 | 
				
			||||||
        subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
 | 
					        subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
 | 
				
			||||||
@ -216,6 +219,8 @@ void subghz_free(SubGhz* subghz) {
 | 
				
			|||||||
        subghz->rpc_ctx = NULL;
 | 
					        subghz->rpc_ctx = NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subghz_speaker_off(subghz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Packet Test
 | 
					    // Packet Test
 | 
				
			||||||
    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
 | 
					    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
 | 
				
			||||||
    subghz_test_packet_free(subghz->subghz_test_packet);
 | 
					    subghz_test_packet_free(subghz->subghz_test_packet);
 | 
				
			||||||
 | 
				
			|||||||
@ -257,6 +257,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
    subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
 | 
					    subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
 | 
				
			||||||
    subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
					    subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
				
			||||||
        environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
					        environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
				
			||||||
 | 
					    subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
 | 
				
			||||||
 | 
					        environment, EXT_PATH("subghz/assets/alutech_at_4n"));
 | 
				
			||||||
    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
					    subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
				
			||||||
        environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
					        environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
				
			||||||
    subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
 | 
					    subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
 | 
				
			||||||
@ -309,6 +311,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
    free(instance);
 | 
					    free(instance);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
 | 
				
			||||||
 | 
					    UNUSED(context);
 | 
				
			||||||
 | 
					    uint32_t frequency = 433920000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(furi_string_size(args)) {
 | 
				
			||||||
 | 
					        int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
 | 
				
			||||||
 | 
					        if(ret != 1) {
 | 
				
			||||||
 | 
					            printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
 | 
				
			||||||
 | 
					            cli_print_usage("subghz rx", "<Frequency: in Hz>", furi_string_get_cstr(args));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!furi_hal_subghz_is_frequency_valid(frequency)) {
 | 
				
			||||||
 | 
					            printf(
 | 
				
			||||||
 | 
					                "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
 | 
				
			||||||
 | 
					                frequency);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Allocate context and buffers
 | 
				
			||||||
 | 
					    SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
 | 
				
			||||||
 | 
					    instance->stream =
 | 
				
			||||||
 | 
					        furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
 | 
				
			||||||
 | 
					    furi_check(instance->stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Configure radio
 | 
				
			||||||
 | 
					    furi_hal_subghz_reset();
 | 
				
			||||||
 | 
					    furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async);
 | 
				
			||||||
 | 
					    frequency = furi_hal_subghz_set_frequency_and_path(frequency);
 | 
				
			||||||
 | 
					    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_power_suppress_charge_enter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Prepare and start RX
 | 
				
			||||||
 | 
					    furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Wait for packets to arrive
 | 
				
			||||||
 | 
					    printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
 | 
				
			||||||
 | 
					    LevelDuration level_duration;
 | 
				
			||||||
 | 
					    size_t counter = 0;
 | 
				
			||||||
 | 
					    while(!cli_cmd_interrupt_received(cli)) {
 | 
				
			||||||
 | 
					        int ret = furi_stream_buffer_receive(
 | 
				
			||||||
 | 
					            instance->stream, &level_duration, sizeof(LevelDuration), 10);
 | 
				
			||||||
 | 
					        if(ret == 0) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(ret != sizeof(LevelDuration)) {
 | 
				
			||||||
 | 
					            puts("stream corrupt");
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(level_duration_is_reset(level_duration)) {
 | 
				
			||||||
 | 
					            puts(". ");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            bool level = level_duration_get_level(level_duration);
 | 
				
			||||||
 | 
					            uint32_t duration = level_duration_get_duration(level_duration);
 | 
				
			||||||
 | 
					            printf("%c%lu ", level ? '+' : '-', duration);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        furi_thread_stdout_flush();
 | 
				
			||||||
 | 
					        counter++;
 | 
				
			||||||
 | 
					        if(counter > 255) {
 | 
				
			||||||
 | 
					            puts("\r\n");
 | 
				
			||||||
 | 
					            counter = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Shutdown radio
 | 
				
			||||||
 | 
					    furi_hal_subghz_stop_async_rx();
 | 
				
			||||||
 | 
					    furi_hal_subghz_sleep();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    furi_hal_power_suppress_charge_exit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Cleanup
 | 
				
			||||||
 | 
					    furi_stream_buffer_free(instance->stream);
 | 
				
			||||||
 | 
					    free(instance);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
 | 
					void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
 | 
				
			||||||
    UNUSED(context);
 | 
					    UNUSED(context);
 | 
				
			||||||
    FuriString* file_name;
 | 
					    FuriString* file_name;
 | 
				
			||||||
@ -377,6 +454,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
					        subghz_environment_set_came_atomo_rainbow_table_file_name(
 | 
				
			||||||
            environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
					            environment, EXT_PATH("subghz/assets/came_atomo"));
 | 
				
			||||||
 | 
					        subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
 | 
				
			||||||
 | 
					            environment, EXT_PATH("subghz/assets/alutech_at_4n"));
 | 
				
			||||||
        subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
					        subghz_environment_set_nice_flor_s_rainbow_table_file_name(
 | 
				
			||||||
            environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
					            environment, EXT_PATH("subghz/assets/nice_flor_s"));
 | 
				
			||||||
        subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
 | 
					        subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
 | 
				
			||||||
@ -431,7 +510,8 @@ static void subghz_cli_command_print_usage() {
 | 
				
			|||||||
    printf("\tchat <frequency:in Hz>\t - Chat with other Flippers\r\n");
 | 
					    printf("\tchat <frequency:in Hz>\t - Chat with other Flippers\r\n");
 | 
				
			||||||
    printf(
 | 
					    printf(
 | 
				
			||||||
        "\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count>\t - Transmitting key\r\n");
 | 
					        "\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count>\t - Transmitting key\r\n");
 | 
				
			||||||
    printf("\trx <frequency:in Hz>\t - Reception key\r\n");
 | 
					    printf("\trx <frequency:in Hz>\t - Receive\r\n");
 | 
				
			||||||
 | 
					    printf("\trx_raw <frequency:in Hz>\t - Receive RAW\r\n");
 | 
				
			||||||
    printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
 | 
					    printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
 | 
					    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
 | 
				
			||||||
@ -733,6 +813,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
 | 
				
			|||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(furi_string_cmp_str(cmd, "rx_raw") == 0) {
 | 
				
			||||||
 | 
					            subghz_cli_command_rx_raw(cli, args, context);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
 | 
					        if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
 | 
				
			||||||
            subghz_cli_command_decode_raw(cli, args, context);
 | 
					            subghz_cli_command_decode_raw(cli, args, context);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
#include <furi.h>
 | 
					#include <furi.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SUBGHZ_HISTORY_MAX 50
 | 
					#define SUBGHZ_HISTORY_MAX 50
 | 
				
			||||||
 | 
					#define SUBGHZ_HISTORY_FREE_HEAP 20480
 | 
				
			||||||
#define TAG "SubGhzHistory"
 | 
					#define TAG "SubGhzHistory"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
@ -121,8 +122,12 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) {
 | 
					bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) {
 | 
				
			||||||
    furi_assert(instance);
 | 
					    furi_assert(instance);
 | 
				
			||||||
 | 
					    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {
 | 
				
			||||||
 | 
					        if(output != NULL) furi_string_printf(output, "    Free heap LOW");
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
 | 
					    if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
 | 
				
			||||||
        if(output != NULL) furi_string_printf(output, "Memory is FULL");
 | 
					        if(output != NULL) furi_string_printf(output, "   Memory is FULL");
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if(output != NULL)
 | 
					    if(output != NULL)
 | 
				
			||||||
@ -142,6 +147,7 @@ bool subghz_history_add_to_history(
 | 
				
			|||||||
    furi_assert(instance);
 | 
					    furi_assert(instance);
 | 
				
			||||||
    furi_assert(context);
 | 
					    furi_assert(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false;
 | 
				
			||||||
    if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
 | 
					    if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SubGhzProtocolDecoderBase* decoder_base = context;
 | 
					    SubGhzProtocolDecoderBase* decoder_base = context;
 | 
				
			||||||
@ -200,27 +206,31 @@ bool subghz_history_add_to_history(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        uint8_t key_data[sizeof(uint64_t)] = {0};
 | 
					        uint8_t key_data[sizeof(uint64_t)] = {0};
 | 
				
			||||||
        if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) {
 | 
					        if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) {
 | 
				
			||||||
            FURI_LOG_E(TAG, "Missing Key");
 | 
					            FURI_LOG_D(TAG, "No Key");
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        uint64_t data = 0;
 | 
					        uint64_t data = 0;
 | 
				
			||||||
        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
 | 
					        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
 | 
				
			||||||
            data = (data << 8) | key_data[i];
 | 
					            data = (data << 8) | key_data[i];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(!(uint32_t)(data >> 32)) {
 | 
					        if(data != 0) {
 | 
				
			||||||
            furi_string_printf(
 | 
					            if(!(uint32_t)(data >> 32)) {
 | 
				
			||||||
                item->item_str,
 | 
					                furi_string_printf(
 | 
				
			||||||
                "%s %lX",
 | 
					                    item->item_str,
 | 
				
			||||||
                furi_string_get_cstr(instance->tmp_string),
 | 
					                    "%s %lX",
 | 
				
			||||||
                (uint32_t)(data & 0xFFFFFFFF));
 | 
					                    furi_string_get_cstr(instance->tmp_string),
 | 
				
			||||||
 | 
					                    (uint32_t)(data & 0xFFFFFFFF));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                furi_string_printf(
 | 
				
			||||||
 | 
					                    item->item_str,
 | 
				
			||||||
 | 
					                    "%s %lX%08lX",
 | 
				
			||||||
 | 
					                    furi_string_get_cstr(instance->tmp_string),
 | 
				
			||||||
 | 
					                    (uint32_t)(data >> 32),
 | 
				
			||||||
 | 
					                    (uint32_t)(data & 0xFFFFFFFF));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            furi_string_printf(
 | 
					            furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string));
 | 
				
			||||||
                item->item_str,
 | 
					 | 
				
			||||||
                "%s %lX%08lX",
 | 
					 | 
				
			||||||
                furi_string_get_cstr(instance->tmp_string),
 | 
					 | 
				
			||||||
                (uint32_t)(data >> 32),
 | 
					 | 
				
			||||||
                (uint32_t)(data & 0xFFFFFFFF));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } while(false);
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    furi_string_free(text);
 | 
					    furi_string_free(text);
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,7 @@ struct SubGhzTxRx {
 | 
				
			|||||||
    SubGhzEnvironment* environment;
 | 
					    SubGhzEnvironment* environment;
 | 
				
			||||||
    SubGhzReceiver* receiver;
 | 
					    SubGhzReceiver* receiver;
 | 
				
			||||||
    SubGhzTransmitter* transmitter;
 | 
					    SubGhzTransmitter* transmitter;
 | 
				
			||||||
 | 
					    SubGhzProtocolFlag filter;
 | 
				
			||||||
    SubGhzProtocolDecoderBase* decoder_result;
 | 
					    SubGhzProtocolDecoderBase* decoder_result;
 | 
				
			||||||
    FlipperFormat* fff_data;
 | 
					    FlipperFormat* fff_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,8 @@
 | 
				
			|||||||
#define MENU_ITEMS 4u
 | 
					#define MENU_ITEMS 4u
 | 
				
			||||||
#define UNLOCK_CNT 3
 | 
					#define UNLOCK_CNT 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    FuriString* item_str;
 | 
					    FuriString* item_str;
 | 
				
			||||||
    uint8_t type;
 | 
					    uint8_t type;
 | 
				
			||||||
@ -59,8 +61,24 @@ typedef struct {
 | 
				
			|||||||
    uint16_t list_offset;
 | 
					    uint16_t list_offset;
 | 
				
			||||||
    uint16_t history_item;
 | 
					    uint16_t history_item;
 | 
				
			||||||
    SubGhzViewReceiverBarShow bar_show;
 | 
					    SubGhzViewReceiverBarShow bar_show;
 | 
				
			||||||
 | 
					    uint8_t u_rssi;
 | 
				
			||||||
} SubGhzViewReceiverModel;
 | 
					} SubGhzViewReceiverModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
 | 
				
			||||||
 | 
					    furi_assert(instance);
 | 
				
			||||||
 | 
					    with_view_model(
 | 
				
			||||||
 | 
					        instance->view,
 | 
				
			||||||
 | 
					        SubGhzViewReceiverModel * model,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
 | 
				
			||||||
 | 
					                model->u_rssi = 0;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
 | 
					void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
 | 
				
			||||||
    furi_assert(subghz_receiver);
 | 
					    furi_assert(subghz_receiver);
 | 
				
			||||||
    subghz_receiver->lock_count = 0;
 | 
					    subghz_receiver->lock_count = 0;
 | 
				
			||||||
@ -168,13 +186,22 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s
 | 
				
			|||||||
    canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
 | 
					    canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
 | 
				
			||||||
 | 
					    for(uint8_t i = 1; i < model->u_rssi; i++) {
 | 
				
			||||||
 | 
					        if(i % 5) {
 | 
				
			||||||
 | 
					            canvas_draw_dot(canvas, 46 + i, 50);
 | 
				
			||||||
 | 
					            canvas_draw_dot(canvas, 47 + i, 51);
 | 
				
			||||||
 | 
					            canvas_draw_dot(canvas, 46 + i, 52);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
 | 
					void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
 | 
				
			||||||
    canvas_clear(canvas);
 | 
					    canvas_clear(canvas);
 | 
				
			||||||
    canvas_set_color(canvas, ColorBlack);
 | 
					    canvas_set_color(canvas, ColorBlack);
 | 
				
			||||||
    canvas_set_font(canvas, FontSecondary);
 | 
					    canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elements_button_left(canvas, "Config");
 | 
					    elements_button_left(canvas, "Config");
 | 
				
			||||||
    canvas_draw_line(canvas, 46, 51, 125, 51);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool scrollbar = model->history_item > 4;
 | 
					    bool scrollbar = model->history_item > 4;
 | 
				
			||||||
    FuriString* str_buff;
 | 
					    FuriString* str_buff;
 | 
				
			||||||
@ -206,11 +233,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
 | 
				
			|||||||
    if(model->history_item == 0) {
 | 
					    if(model->history_item == 0) {
 | 
				
			||||||
        canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
 | 
					        canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					        canvas_set_font(canvas, FontPrimary);
 | 
				
			||||||
        canvas_draw_str(canvas, 63, 46, "Scanning...");
 | 
					        canvas_draw_str(canvas, 63, 44, "Scanning...");
 | 
				
			||||||
        canvas_draw_line(canvas, 46, 51, 125, 51);
 | 
					 | 
				
			||||||
        canvas_set_font(canvas, FontSecondary);
 | 
					        canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subghz_view_rssi_draw(canvas, model);
 | 
				
			||||||
    switch(model->bar_show) {
 | 
					    switch(model->bar_show) {
 | 
				
			||||||
    case SubGhzViewReceiverBarShowLock:
 | 
					    case SubGhzViewReceiverBarShowLock:
 | 
				
			||||||
        canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
 | 
					        canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,8 @@ typedef struct SubGhzViewReceiver SubGhzViewReceiver;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context);
 | 
					typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard);
 | 
					void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void subghz_view_receiver_set_callback(
 | 
					void subghz_view_receiver_set_callback(
 | 
				
			||||||
 | 
				
			|||||||
@ -79,7 +79,6 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x
 | 
				
			|||||||
void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
 | 
					void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
 | 
				
			||||||
    uint8_t column_height = 6;
 | 
					    uint8_t column_height = 6;
 | 
				
			||||||
    if(rssi) {
 | 
					    if(rssi) {
 | 
				
			||||||
        //rssi = rssi
 | 
					 | 
				
			||||||
        if(rssi > 54) rssi = 54;
 | 
					        if(rssi > 54) rssi = 54;
 | 
				
			||||||
        for(uint8_t i = 1; i < rssi; i++) {
 | 
					        for(uint8_t i = 1; i < rssi; i++) {
 | 
				
			||||||
            if(i % 5) {
 | 
					            if(i % 5) {
 | 
				
			||||||
 | 
				
			|||||||
@ -84,9 +84,10 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
 | 
				
			|||||||
    canvas_clear(canvas);
 | 
					    canvas_clear(canvas);
 | 
				
			||||||
    canvas_set_color(canvas, ColorBlack);
 | 
					    canvas_set_color(canvas, ColorBlack);
 | 
				
			||||||
    canvas_set_font(canvas, FontSecondary);
 | 
					    canvas_set_font(canvas, FontSecondary);
 | 
				
			||||||
    elements_multiline_text(canvas, 0, 8, furi_string_get_cstr(model->key_str));
 | 
					    elements_multiline_text_aligned(
 | 
				
			||||||
    canvas_draw_str(canvas, 78, 8, furi_string_get_cstr(model->frequency_str));
 | 
					        canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str));
 | 
				
			||||||
    canvas_draw_str(canvas, 113, 8, furi_string_get_cstr(model->preset_str));
 | 
					    canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
 | 
				
			||||||
 | 
					    canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
 | 
				
			||||||
    if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send");
 | 
					    if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
#include "../u2f_app_i.h"
 | 
					#include "../u2f_app_i.h"
 | 
				
			||||||
#include "../views/u2f_view.h"
 | 
					#include "../views/u2f_view.h"
 | 
				
			||||||
#include <dolphin/dolphin.h>
 | 
					#include <dolphin/dolphin.h>
 | 
				
			||||||
#include "furi_hal.h"
 | 
					#include <furi_hal.h>
 | 
				
			||||||
#include "../u2f.h"
 | 
					#include "../u2f.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define U2F_REQUEST_TIMEOUT 500
 | 
					#define U2F_REQUEST_TIMEOUT 500
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF10_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 172 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF11_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 173 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF12_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 180 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF1_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 177 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF2_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 179 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF3_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 178 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF4_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 177 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF5_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 178 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF6_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 177 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF7_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 176 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF8_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 176 B  | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/assets/ButtonF9_5x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 179 B  | 
@ -46,11 +46,25 @@ typedef struct {
 | 
				
			|||||||
#define KEY_WIDTH 9
 | 
					#define KEY_WIDTH 9
 | 
				
			||||||
#define KEY_HEIGHT 12
 | 
					#define KEY_HEIGHT 12
 | 
				
			||||||
#define KEY_PADDING 1
 | 
					#define KEY_PADDING 1
 | 
				
			||||||
#define ROW_COUNT 6
 | 
					#define ROW_COUNT 7
 | 
				
			||||||
#define COLUMN_COUNT 12
 | 
					#define COLUMN_COUNT 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 0 width items are not drawn, but there value is used
 | 
					// 0 width items are not drawn, but there value is used
 | 
				
			||||||
const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
 | 
					const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11},
 | 
				
			||||||
 | 
					        {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
 | 
					        {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
 | 
				
			||||||
        {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
 | 
					        {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
 | 
				
			||||||
@ -224,7 +238,12 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    canvas_set_font(canvas, FontKeyboard);
 | 
					    canvas_set_font(canvas, FontKeyboard);
 | 
				
			||||||
    // Start shifting the all keys up if on the next row (Scrolling)
 | 
					    // Start shifting the all keys up if on the next row (Scrolling)
 | 
				
			||||||
    uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
 | 
					    uint8_t initY = model->y == 0 ? 0 : 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(model->y > 5) {
 | 
				
			||||||
 | 
					        initY = model->y - 4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for(uint8_t y = initY; y < ROW_COUNT; y++) {
 | 
					    for(uint8_t y = initY; y < ROW_COUNT; y++) {
 | 
				
			||||||
        const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
 | 
					        const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
 | 
				
			||||||
        uint8_t x = 0;
 | 
					        uint8_t x = 0;
 | 
				
			||||||
@ -365,7 +384,10 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
 | 
				
			|||||||
    with_view_model(
 | 
					    with_view_model(
 | 
				
			||||||
        hid_keyboard->view,
 | 
					        hid_keyboard->view,
 | 
				
			||||||
        HidKeyboardModel * model,
 | 
					        HidKeyboardModel * model,
 | 
				
			||||||
        { model->transport = bt_hid->transport; },
 | 
					        {
 | 
				
			||||||
 | 
					            model->transport = bt_hid->transport;
 | 
				
			||||||
 | 
					            model->y = 1;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        true);
 | 
					        true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return hid_keyboard;
 | 
					    return hid_keyboard;
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@
 | 
				
			|||||||
#include <storage/storage.h>
 | 
					#include <storage/storage.h>
 | 
				
			||||||
#include <lib/flipper_format/flipper_format.h>
 | 
					#include <lib/flipper_format/flipper_format.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
#include <m-array.h>
 | 
					#include <m-array.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define TAG "MusicPlayerWorker"
 | 
					#define TAG "MusicPlayerWorker"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ App(
 | 
				
			|||||||
    appid="nfc_magic",
 | 
					    appid="nfc_magic",
 | 
				
			||||||
    name="Nfc Magic",
 | 
					    name="Nfc Magic",
 | 
				
			||||||
    apptype=FlipperAppType.EXTERNAL,
 | 
					    apptype=FlipperAppType.EXTERNAL,
 | 
				
			||||||
 | 
					    targets=["f7"],
 | 
				
			||||||
    entry_point="nfc_magic_app",
 | 
					    entry_point="nfc_magic_app",
 | 
				
			||||||
    requires=[
 | 
					    requires=[
 | 
				
			||||||
        "storage",
 | 
					        "storage",
 | 
				
			||||||
 | 
				
			|||||||
@ -136,9 +136,9 @@ void nfc_magic_free(NfcMagic* nfc_magic) {
 | 
				
			|||||||
    free(nfc_magic);
 | 
					    free(nfc_magic);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const NotificationSequence nfc_magic_sequence_blink_start_blue = {
 | 
					static const NotificationSequence nfc_magic_sequence_blink_start_cyan = {
 | 
				
			||||||
    &message_blink_start_10,
 | 
					    &message_blink_start_10,
 | 
				
			||||||
    &message_blink_set_color_blue,
 | 
					    &message_blink_set_color_cyan,
 | 
				
			||||||
    &message_do_not_reset,
 | 
					    &message_do_not_reset,
 | 
				
			||||||
    NULL,
 | 
					    NULL,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -149,7 +149,7 @@ static const NotificationSequence nfc_magic_sequence_blink_stop = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nfc_magic_blink_start(NfcMagic* nfc_magic) {
 | 
					void nfc_magic_blink_start(NfcMagic* nfc_magic) {
 | 
				
			||||||
    notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue);
 | 
					    notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_cyan);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nfc_magic_blink_stop(NfcMagic* nfc_magic) {
 | 
					void nfc_magic_blink_stop(NfcMagic* nfc_magic) {
 | 
				
			||||||
 | 
				
			|||||||