new iButton app (#328)
* rename old ibutton app to ibutton-test * more renames * updated onewire library compilation condition * add submenu_clean subroutine * add index for submenu callback * c++ guard for gui modules * add released ibutton app * fix the position of the submenu window if there are too few items * iButton app basis * negative icon position info * fix submenu_clean subroutine * add ibutton app to applications makefile * add onewire key read routine to read mode * rename mode to scene * rename files and folder (mode to scene) * rename ibutton view to view manager * rename get_view to get_view_manager * cpp guards * key read, store and notify features * syntax fix * make iButtonScene functions pure virtual * fix syntax * add text store, add new scene (crc error) * not a key scene * syntax fix * read success scene * app, switching to the previous scene with the number of scenes to be skipped * scene whith menu when key is readed * fix font height calculation, fix offsets * add key write scene * view_dispatcher_remove_view subroutine * generic pause/resume os methods * fix furi_assert usage * key store, worker * fix pointer comparsion * saved keys, saved key action scenes * key delete/confirm delete scenes and routines * use last input subsystem changes * fix syntax * fix new model usage in submenu * fix includes * use vibro pin * use stored key name if valid * emulate scene * random name generator * name and save readed key scenes, new icon * fix icon position * fix text scene exit * fix naming, fix text placement, new info scene * state-driven cyfral decoder * better cyfral decoder * better cyfral decoder * one wire: search command set * metakom decoder * more key types * add next scene to error scenes * universal key reader * use new key reader * syntax fix * warning fix * byte input module template * new thread and insomnia api usage * New element: slightly rounded frame * Use elements_slightly_rounded_frame in text input * Gui test app: byte input usage * Byte input module: data drawing and selection * Byte input: comment currently unused fns * remove volatile qualifier * base byte input realisation * App gui test: remove internal fns visibility * Byne input, final version * test install gcc-arm-none-eabi-10-2020-q4-major * test install gcc-arm-none-eabi-10-2020-q4-major * App iButton: byte input view managment * App iButton: add key manually scenes * App iButton: rename scenes, add popup timeout * App iButton: use new scenes, new fn for rollback to specific prevous scene. * App iButton: remove byte input view on app exit * App iButton: edit key scene * Module byte input: reduce swintch value to uint8_t * Module byte input: switch from switch-case to if, unfortunately we need compile-time constants to use with switch * Icons: new small arrows * Module byte input: new arrangement of elements * OneWire slave lib: fix deattach sequence * App iButton: pulse sequencer * App iButton: add more keys to store * App iButton: split key worker to separate read/write/emulate entitys * App iButton: use new read/emulate entities * fix callback pointer saving * App iButton: use KeyReader error enum instead of KeyWorker error list handling * App iButton: do not use insomnia fns in pulse sequencer * App iButton: use KeyReader error enum in read scene * OneWire slave lib: more READ ROM command variants, call callback only if positive result * GPIO resources: add external gpio * App SD/NFC: removed application * App iButton-test: update to new light api * App iButton: update to new light-api * Outdated apps: add api-light-usage * Gpio: update SD card CS pin settings * API-power: added fns to disable/enable external 3v3 dc-dc * API-gpio: separated SD card detect routines * Resources: removed sd cs pin * SD card: low level init now resets card power supply * App SD-filesystem: use new card detect fns * SD card: fix low level init headers * SD card: more realilable low level init, power reset, exit from command read cycle conditionally * App SD-filesystem: led notifiers, init cycling * SD card: backport to F4 * Api PWM: add c++ guards * App iButton: yellow blink in emulate scene, vibro on * App iButton: one wire keys command set * App iButton: successful write scene * App iButton: key writer * App iButton: syntax fix * App iButton: notify write success * App iButton: fix double scene change * SD card: handle eject in init sequence * SD card: api to set level on detect gpio * SPI: api to set state on bus pins * SD card: set low state on bus pins while power reset * File select: init * File select: fix input consuming * SD Card: fixed dir open api error * SD-card: replace strncpy by strlcpy. Fix buffer overflow error. * API HAL OS: replace CMP based ticks with ARR based one, hard reset lptimer on reconfiguration. * GUI: More stack size for (temporary, wee need to implement sd card api in separate thread) * GUI: File select module. * App iButton-test: remove obsolete app Co-authored-by: rusdacent <rusdacentx0x08@gmail.com> Co-authored-by: coreglitch <mail@s3f.ru> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									fc12f91a64
								
							
						
					
					
						commit
						1daef3d025
					
				| @ -32,7 +32,6 @@ int32_t sdnfc(void* p); | |||||||
| int32_t floopper_bloopper(void* p); | int32_t floopper_bloopper(void* p); | ||||||
| int32_t sd_filesystem(void* p); | int32_t sd_filesystem(void* p); | ||||||
| int32_t app_subghz(void* p); | int32_t app_subghz(void* p); | ||||||
| 
 |  | ||||||
| int32_t gui_test(void* p); | int32_t gui_test(void* p); | ||||||
| int32_t keypad_test(void* p); | int32_t keypad_test(void* p); | ||||||
| 
 | 
 | ||||||
| @ -58,7 +57,8 @@ const FlipperApplication FLIPPER_SERVICES[] = { | |||||||
|      .name = "backlight_control", |      .name = "backlight_control", | ||||||
|      .stack_size = 1024, |      .stack_size = 1024, | ||||||
|      .icon = A_Plugins_14}, |      .icon = A_Plugins_14}, | ||||||
|     {.app = gui_task, .name = "gui_task", .stack_size = 1024, .icon = A_Plugins_14}, |     // TODO: fix stack size when sd api will be in separate thread
 | ||||||
|  |     {.app = gui_task, .name = "gui_task", .stack_size = 8192, .icon = A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_MENU | #ifdef APP_MENU | ||||||
| @ -132,7 +132,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IBUTTON | #ifdef APP_IBUTTON | ||||||
|     {.app = app_ibutton, .name = "ibutton", .stack_size = 1024, .icon = A_Plugins_14}, |     {.app = app_ibutton, .name = "ibutton", .stack_size = 4096, .icon = A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_GPIO_DEMO | #ifdef APP_GPIO_DEMO | ||||||
|  | |||||||
| @ -278,6 +278,7 @@ BUILD_IBUTTON ?= 0 | |||||||
| ifeq ($(BUILD_IBUTTON), 1) | ifeq ($(BUILD_IBUTTON), 1) | ||||||
| CFLAGS		+= -DBUILD_IBUTTON | CFLAGS		+= -DBUILD_IBUTTON | ||||||
| CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*.cpp) | CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*.cpp) | ||||||
|  | CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*/*.cpp) | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| APP_GUI_TEST ?= 0 | APP_GUI_TEST ?= 0 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <gui/modules/dialog_ex.h> | #include <gui/modules/dialog_ex.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/text_input.h> | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/byte_input.h> | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -15,6 +16,7 @@ typedef enum { | |||||||
|     GuiTesterViewDialog, |     GuiTesterViewDialog, | ||||||
|     GuiTesterViewDialogEx, |     GuiTesterViewDialogEx, | ||||||
|     GuiTesterViewPopup, |     GuiTesterViewPopup, | ||||||
|  |     GuiTesterViewByteInput, | ||||||
|     GuiTesterViewLast |     GuiTesterViewLast | ||||||
| } GuiTesterView; | } GuiTesterView; | ||||||
| 
 | 
 | ||||||
| @ -25,13 +27,14 @@ typedef struct { | |||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|     Popup* popup; |     Popup* popup; | ||||||
|  |     ByteInput* byte_input; | ||||||
|     GuiTesterView view_index; |     GuiTesterView view_index; | ||||||
| } GuiTester; | } GuiTester; | ||||||
| 
 | 
 | ||||||
| GuiTester* gui_test_alloc(void) { | static GuiTester* gui_test_alloc(void) { | ||||||
|     GuiTester* gui_tester = furi_alloc(sizeof(GuiTester)); |     GuiTester* gui_tester = furi_alloc(sizeof(GuiTester)); | ||||||
|     gui_tester->view_dispatcher = view_dispatcher_alloc(); |     gui_tester->view_dispatcher = view_dispatcher_alloc(); | ||||||
|     gui_tester->view_index = GuiTesterViewDialogEx; |     gui_tester->view_index = GuiTesterViewByteInput; | ||||||
| 
 | 
 | ||||||
|     gui_tester->dialog = dialog_alloc(); |     gui_tester->dialog = dialog_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -57,10 +60,16 @@ GuiTester* gui_test_alloc(void) { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         gui_tester->view_dispatcher, GuiTesterViewPopup, popup_get_view(gui_tester->popup)); |         gui_tester->view_dispatcher, GuiTesterViewPopup, popup_get_view(gui_tester->popup)); | ||||||
| 
 | 
 | ||||||
|  |     gui_tester->byte_input = byte_input_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         gui_tester->view_dispatcher, | ||||||
|  |         GuiTesterViewByteInput, | ||||||
|  |         byte_input_get_view(gui_tester->byte_input)); | ||||||
|  | 
 | ||||||
|     return gui_tester; |     return gui_tester; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void next_view(void* context) { | static void next_view(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     GuiTester* gui_tester = context; |     GuiTester* gui_tester = context; | ||||||
| 
 | 
 | ||||||
| @ -72,23 +81,27 @@ void next_view(void* context) { | |||||||
|     view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index); |     view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void popup_callback(void* context) { | static void popup_callback(void* context) { | ||||||
|     next_view(context); |     next_view(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void submenu_callback(void* context, uint32_t index) { | static void submenu_callback(void* context, uint32_t index) { | ||||||
|     next_view(context); |     next_view(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dialog_callback(DialogResult result, void* context) { | static void dialog_callback(DialogResult result, void* context) { | ||||||
|     next_view(context); |     next_view(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dialog_ex_callback(DialogExResult result, void* context) { | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|     next_view(context); |     next_view(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void text_input_callback(void* context, char* text) { | static void text_input_callback(void* context, char* text) { | ||||||
|  |     next_view(context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void byte_input_callback(void* context, uint8_t* bytes, uint8_t bytes_count) { | ||||||
|     next_view(context); |     next_view(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -150,6 +163,34 @@ int32_t gui_test(void* param) { | |||||||
|         text_input_text_len); |         text_input_text_len); | ||||||
|     text_input_set_header_text(gui_tester->text_input, "Name the key"); |     text_input_set_header_text(gui_tester->text_input, "Name the key"); | ||||||
| 
 | 
 | ||||||
|  |     const uint8_t byte_input_bytes_len = 16; | ||||||
|  |     uint8_t byte_input_bytes[16] = { | ||||||
|  |         0x00, | ||||||
|  |         0x01, | ||||||
|  |         0x02, | ||||||
|  |         0x03, | ||||||
|  |         0x04, | ||||||
|  |         0x05, | ||||||
|  |         0x06, | ||||||
|  |         0x07, | ||||||
|  |         0x08, | ||||||
|  |         0x09, | ||||||
|  |         0x0A, | ||||||
|  |         0x0B, | ||||||
|  |         0x0C, | ||||||
|  |         0x0D, | ||||||
|  |         0x0E, | ||||||
|  |         0x0F}; | ||||||
|  | 
 | ||||||
|  |     byte_input_set_result_callback( | ||||||
|  |         gui_tester->byte_input, | ||||||
|  |         byte_input_callback, | ||||||
|  |         NULL, | ||||||
|  |         gui_tester, | ||||||
|  |         byte_input_bytes, | ||||||
|  |         byte_input_bytes_len); | ||||||
|  |     byte_input_set_header_text(gui_tester->byte_input, "Enter the key"); | ||||||
|  | 
 | ||||||
|     view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index); |     view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  | |||||||
| @ -135,7 +135,7 @@ void elements_multiline_text_aligned( | |||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     furi_assert(text); |     furi_assert(text); | ||||||
| 
 | 
 | ||||||
|     uint8_t font_height = canvas_current_font_height(canvas); |     uint8_t font_height = canvas_current_font_height(canvas) + 2; | ||||||
|     string_t str; |     string_t str; | ||||||
|     string_init(str); |     string_init(str); | ||||||
|     const char* start = text; |     const char* start = text; | ||||||
| @ -192,4 +192,20 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text) { | |||||||
|         y += font_height; |         y += font_height; | ||||||
|     } while(end); |     } while(end); | ||||||
|     string_clear(str); |     string_clear(str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void elements_slightly_rounded_frame( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     uint8_t width, | ||||||
|  |     uint8_t height) { | ||||||
|  |     furi_assert(canvas); | ||||||
|  |     canvas_draw_frame(canvas, x, y, width, height); | ||||||
|  |     canvas_invert_color(canvas); | ||||||
|  |     canvas_draw_dot(canvas, x, y); | ||||||
|  |     canvas_draw_dot(canvas, x + width - 1, y + height - 1); | ||||||
|  |     canvas_draw_dot(canvas, x + width - 1, y); | ||||||
|  |     canvas_draw_dot(canvas, x, y + height - 1); | ||||||
|  |     canvas_invert_color(canvas); | ||||||
| } | } | ||||||
| @ -61,6 +61,18 @@ void elements_multiline_text_aligned( | |||||||
|  */ |  */ | ||||||
| void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text); | void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Draw slightly rounded frame | ||||||
|  |  * @param x, y - top left corner coordinates | ||||||
|  |  * @param width, height - size of frame | ||||||
|  |  */ | ||||||
|  | void elements_slightly_rounded_frame( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     uint8_t width, | ||||||
|  |     uint8_t height); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
							
								
								
									
										765
									
								
								applications/gui/modules/byte_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										765
									
								
								applications/gui/modules/byte_input.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,765 @@ | |||||||
|  | #include "byte_input.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | struct ByteInput { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const uint8_t value; | ||||||
|  |     const uint8_t x; | ||||||
|  |     const uint8_t y; | ||||||
|  | } ByteInputKey; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* header; | ||||||
|  |     uint8_t* bytes; | ||||||
|  |     uint8_t bytes_count; | ||||||
|  | 
 | ||||||
|  |     ByteInputCallback input_callback; | ||||||
|  |     ByteChangedCallback changed_callback; | ||||||
|  |     void* callback_context; | ||||||
|  | 
 | ||||||
|  |     bool selected_high_nibble; | ||||||
|  |     uint8_t selected_byte; | ||||||
|  |     int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard
 | ||||||
|  |     uint8_t selected_column; | ||||||
|  |     uint8_t first_visible_byte; | ||||||
|  | } ByteInputModel; | ||||||
|  | 
 | ||||||
|  | #ifndef MAX | ||||||
|  | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef MIN | ||||||
|  | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static const uint8_t keyboard_origin_x = 8; | ||||||
|  | static const uint8_t keyboard_origin_y = 32; | ||||||
|  | static const uint8_t keyboard_row_count = 2; | ||||||
|  | static const uint8_t enter_symbol = '\r'; | ||||||
|  | static const uint8_t backspace_symbol = '\b'; | ||||||
|  | static const uint8_t max_drawable_bytes = 8; | ||||||
|  | 
 | ||||||
|  | static const ByteInputKey keyboard_keys_row_1[] = { | ||||||
|  |     {'0', 0, 12}, | ||||||
|  |     {'1', 11, 12}, | ||||||
|  |     {'2', 22, 12}, | ||||||
|  |     {'3', 33, 12}, | ||||||
|  |     {'4', 44, 12}, | ||||||
|  |     {'5', 55, 12}, | ||||||
|  |     {'6', 66, 12}, | ||||||
|  |     {'7', 77, 12}, | ||||||
|  |     {backspace_symbol, 101, 4}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const ByteInputKey keyboard_keys_row_2[] = { | ||||||
|  |     {'8', 0, 26}, | ||||||
|  |     {'9', 11, 26}, | ||||||
|  |     {'A', 22, 26}, | ||||||
|  |     {'B', 33, 26}, | ||||||
|  |     {'C', 44, 26}, | ||||||
|  |     {'D', 55, 26}, | ||||||
|  |     {'E', 66, 26}, | ||||||
|  |     {'F', 77, 26}, | ||||||
|  |     {enter_symbol, 93, 17}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get row size | ||||||
|  |  *  | ||||||
|  |  * @param row_index Index of row  | ||||||
|  |  * @return uint8_t Row size | ||||||
|  |  */ | ||||||
|  | static uint8_t byte_input_get_row_size(uint8_t row_index) { | ||||||
|  |     uint8_t row_size = 0; | ||||||
|  | 
 | ||||||
|  |     switch(row_index + 1) { | ||||||
|  |     case 1: | ||||||
|  |         row_size = sizeof(keyboard_keys_row_1) / sizeof(ByteInputKey); | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         row_size = sizeof(keyboard_keys_row_2) / sizeof(ByteInputKey); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return row_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get row pointer | ||||||
|  |  *  | ||||||
|  |  * @param row_index Index of row  | ||||||
|  |  * @return const ByteInputKey* Row pointer | ||||||
|  |  */ | ||||||
|  | static const ByteInputKey* byte_input_get_row(uint8_t row_index) { | ||||||
|  |     const ByteInputKey* row = NULL; | ||||||
|  | 
 | ||||||
|  |     switch(row_index + 1) { | ||||||
|  |     case 1: | ||||||
|  |         row = keyboard_keys_row_1; | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         row = keyboard_keys_row_2; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return row; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get text from nibble | ||||||
|  |  *  | ||||||
|  |  * @param byte byte value | ||||||
|  |  * @param high_nibble Get from high nibble, otherwise low nibble | ||||||
|  |  * @return char nibble text | ||||||
|  |  */ | ||||||
|  | static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { | ||||||
|  |     if(high_nibble) { | ||||||
|  |         byte = byte >> 4; | ||||||
|  |     } | ||||||
|  |     byte = byte & 0x0F; | ||||||
|  | 
 | ||||||
|  |     switch(byte & 0x0F) { | ||||||
|  |     case 0x0: | ||||||
|  |     case 0x1: | ||||||
|  |     case 0x2: | ||||||
|  |     case 0x3: | ||||||
|  |     case 0x4: | ||||||
|  |     case 0x5: | ||||||
|  |     case 0x6: | ||||||
|  |     case 0x7: | ||||||
|  |     case 0x8: | ||||||
|  |     case 0x9: | ||||||
|  |         byte = byte + '0'; | ||||||
|  |         break; | ||||||
|  |     case 0xA: | ||||||
|  |     case 0xB: | ||||||
|  |     case 0xC: | ||||||
|  |     case 0xD: | ||||||
|  |     case 0xE: | ||||||
|  |     case 0xF: | ||||||
|  |         byte = byte - 0xA + 'A'; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         byte = '!'; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return byte; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Draw input box (common view) | ||||||
|  |  *  | ||||||
|  |  * @param canvas  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { | ||||||
|  |     const uint8_t text_x = 7; | ||||||
|  |     const uint8_t text_y = 27; | ||||||
|  | 
 | ||||||
|  |     elements_slightly_rounded_frame(canvas, 5, 16, 117, 15); | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = model->first_visible_byte; | ||||||
|  |         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); | ||||||
|  |         i++) { | ||||||
|  |         uint8_t byte_position = i - model->first_visible_byte; | ||||||
|  | 
 | ||||||
|  |         if(i == model->selected_byte) { | ||||||
|  |             canvas_draw_frame(canvas, text_x + byte_position * 14, text_y - 9, 15, 11); | ||||||
|  | 
 | ||||||
|  |             if(model->selected_high_nibble) { | ||||||
|  |                 canvas_draw_glyph( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + 8 + byte_position * 14, | ||||||
|  |                     text_y, | ||||||
|  |                     byte_input_get_nibble_text(model->bytes[i], false)); | ||||||
|  |                 canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 8, 7, 9); | ||||||
|  |                 canvas_invert_color(canvas); | ||||||
|  |                 canvas_draw_line( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + 14 + byte_position * 14, | ||||||
|  |                     text_y - 6, | ||||||
|  |                     text_x + 14 + byte_position * 14, | ||||||
|  |                     text_y - 2); | ||||||
|  |                 canvas_draw_glyph( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + 2 + byte_position * 14, | ||||||
|  |                     text_y, | ||||||
|  |                     byte_input_get_nibble_text(model->bytes[i], true)); | ||||||
|  |                 canvas_invert_color(canvas); | ||||||
|  |             } else { | ||||||
|  |                 canvas_draw_box(canvas, text_x + 7 + byte_position * 14, text_y - 8, 7, 9); | ||||||
|  |                 canvas_draw_glyph( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + 2 + byte_position * 14, | ||||||
|  |                     text_y, | ||||||
|  |                     byte_input_get_nibble_text(model->bytes[i], true)); | ||||||
|  |                 canvas_invert_color(canvas); | ||||||
|  |                 canvas_draw_line( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + byte_position * 14, | ||||||
|  |                     text_y - 6, | ||||||
|  |                     text_x + byte_position * 14, | ||||||
|  |                     text_y - 2); | ||||||
|  |                 canvas_draw_glyph( | ||||||
|  |                     canvas, | ||||||
|  |                     text_x + 8 + byte_position * 14, | ||||||
|  |                     text_y, | ||||||
|  |                     byte_input_get_nibble_text(model->bytes[i], false)); | ||||||
|  |                 canvas_invert_color(canvas); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 2 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], true)); | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 8 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], false)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { | ||||||
|  |         canvas_draw_icon_name(canvas, 123, 21, I_ButtonRightSmall_3x5); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->first_visible_byte > 0) { | ||||||
|  |         canvas_draw_icon_name(canvas, 1, 21, I_ButtonLeftSmall_3x5); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Draw input box (selected view) | ||||||
|  |  *  | ||||||
|  |  * @param canvas  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { | ||||||
|  |     const uint8_t text_x = 7; | ||||||
|  |     const uint8_t text_y = 27; | ||||||
|  | 
 | ||||||
|  |     canvas_draw_box(canvas, 0, 14, 128, 19); | ||||||
|  |     canvas_invert_color(canvas); | ||||||
|  |     elements_slightly_rounded_frame(canvas, 5, 16, 117, 15); | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = model->first_visible_byte; | ||||||
|  |         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); | ||||||
|  |         i++) { | ||||||
|  |         uint8_t byte_position = i - model->first_visible_byte; | ||||||
|  | 
 | ||||||
|  |         if(i == model->selected_byte) { | ||||||
|  |             canvas_draw_box(canvas, text_x + byte_position * 14, text_y - 9, 15, 11); | ||||||
|  |             canvas_invert_color(canvas); | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 2 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], true)); | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 8 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], false)); | ||||||
|  |             canvas_invert_color(canvas); | ||||||
|  |         } else { | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 2 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], true)); | ||||||
|  |             canvas_draw_glyph( | ||||||
|  |                 canvas, | ||||||
|  |                 text_x + 8 + byte_position * 14, | ||||||
|  |                 text_y, | ||||||
|  |                 byte_input_get_nibble_text(model->bytes[i], false)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { | ||||||
|  |         canvas_draw_icon_name(canvas, 123, 21, I_ButtonRightSmall_3x5); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->first_visible_byte > 0) { | ||||||
|  |         canvas_draw_icon_name(canvas, 1, 21, I_ButtonLeftSmall_3x5); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_invert_color(canvas); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set nibble at position | ||||||
|  |  *  | ||||||
|  |  * @param data where to set nibble | ||||||
|  |  * @param position byte position | ||||||
|  |  * @param value char value | ||||||
|  |  * @param high_nibble set high nibble | ||||||
|  |  */ | ||||||
|  | static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { | ||||||
|  |     switch(value) { | ||||||
|  |     case '0': | ||||||
|  |     case '1': | ||||||
|  |     case '2': | ||||||
|  |     case '3': | ||||||
|  |     case '4': | ||||||
|  |     case '5': | ||||||
|  |     case '6': | ||||||
|  |     case '7': | ||||||
|  |     case '8': | ||||||
|  |     case '9': | ||||||
|  |         value = value - '0'; | ||||||
|  |         break; | ||||||
|  |     case 'A': | ||||||
|  |     case 'B': | ||||||
|  |     case 'C': | ||||||
|  |     case 'D': | ||||||
|  |     case 'E': | ||||||
|  |     case 'F': | ||||||
|  |         value = value - 'A' + 10; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         value = 0; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(high_nibble) { | ||||||
|  |         data[position] &= 0x0F; | ||||||
|  |         data[position] |= value << 4; | ||||||
|  |     } else { | ||||||
|  |         data[position] &= 0xF0; | ||||||
|  |         data[position] |= value; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief What currently selected | ||||||
|  |  *  | ||||||
|  |  * @return true - keyboard selected, false - input selected | ||||||
|  |  */ | ||||||
|  | static bool byte_input_keyboard_selected(ByteInputModel* model) { | ||||||
|  |     return model->selected_row >= 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Do transition from keyboard | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_transition_from_keyboard(ByteInputModel* model) { | ||||||
|  |     model->selected_row += 1; | ||||||
|  |     model->selected_high_nibble = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Increase selected byte position | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_inc_selected_byte(ByteInputModel* model) { | ||||||
|  |     if(model->selected_byte < model->bytes_count - 1) { | ||||||
|  |         model->selected_byte += 1; | ||||||
|  | 
 | ||||||
|  |         if(model->bytes_count > max_drawable_bytes) { | ||||||
|  |             if(model->selected_byte - model->first_visible_byte > (max_drawable_bytes - 2)) { | ||||||
|  |                 if(model->first_visible_byte < model->bytes_count - max_drawable_bytes) { | ||||||
|  |                     model->first_visible_byte++; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Decrease selected byte position | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_dec_selected_byte(ByteInputModel* model) { | ||||||
|  |     if(model->selected_byte > 0) { | ||||||
|  |         model->selected_byte -= 1; | ||||||
|  | 
 | ||||||
|  |         if(model->selected_byte - model->first_visible_byte < 1) { | ||||||
|  |             if(model->first_visible_byte > 0) { | ||||||
|  |                 model->first_visible_byte--; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Call input callback | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_call_input_callback(ByteInputModel* model) { | ||||||
|  |     if(model->input_callback != NULL) { | ||||||
|  |         model->input_callback(model->callback_context, model->bytes, model->bytes_count); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Call changed callback | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_call_changed_callback(ByteInputModel* model) { | ||||||
|  |     if(model->changed_callback != NULL) { | ||||||
|  |         model->changed_callback(model->callback_context, model->bytes, model->bytes_count); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle up button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_handle_up(ByteInputModel* model) { | ||||||
|  |     if(model->selected_row > -1) { | ||||||
|  |         model->selected_row -= 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle down button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_handle_down(ByteInputModel* model) { | ||||||
|  |     if(byte_input_keyboard_selected(model)) { | ||||||
|  |         if(model->selected_row < keyboard_row_count - 1) { | ||||||
|  |             model->selected_row += 1; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         byte_input_transition_from_keyboard(model); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle left button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_handle_left(ByteInputModel* model) { | ||||||
|  |     if(byte_input_keyboard_selected(model)) { | ||||||
|  |         if(model->selected_column > 0) { | ||||||
|  |             model->selected_column -= 1; | ||||||
|  |         } else { | ||||||
|  |             model->selected_column = byte_input_get_row_size(model->selected_row) - 1; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         byte_input_dec_selected_byte(model); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle right button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_handle_right(ByteInputModel* model) { | ||||||
|  |     if(byte_input_keyboard_selected(model)) { | ||||||
|  |         if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { | ||||||
|  |             model->selected_column += 1; | ||||||
|  |         } else { | ||||||
|  |             model->selected_column = 0; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         byte_input_inc_selected_byte(model); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle OK button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_handle_ok(ByteInputModel* model) { | ||||||
|  |     if(byte_input_keyboard_selected(model)) { | ||||||
|  |         uint8_t value = byte_input_get_row(model->selected_row)[model->selected_column].value; | ||||||
|  | 
 | ||||||
|  |         if(value == enter_symbol) { | ||||||
|  |             byte_input_call_input_callback(model); | ||||||
|  |         } else if(value == backspace_symbol) { | ||||||
|  |             model->bytes[model->selected_byte] = 0; | ||||||
|  |             model->selected_high_nibble = true; | ||||||
|  |             byte_input_dec_selected_byte(model); | ||||||
|  |             byte_input_call_changed_callback(model); | ||||||
|  |         } else { | ||||||
|  |             byte_input_set_nibble( | ||||||
|  |                 model->bytes, model->selected_byte, value, model->selected_high_nibble); | ||||||
|  |             if(model->selected_high_nibble == true) { | ||||||
|  |                 model->selected_high_nibble = false; | ||||||
|  |             } else { | ||||||
|  |                 byte_input_inc_selected_byte(model); | ||||||
|  |                 model->selected_high_nibble = true; | ||||||
|  |             } | ||||||
|  |             byte_input_call_changed_callback(model); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         byte_input_transition_from_keyboard(model); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Draw callback | ||||||
|  |  *  | ||||||
|  |  * @param canvas  | ||||||
|  |  * @param _model  | ||||||
|  |  */ | ||||||
|  | static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     ByteInputModel* model = _model; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str(canvas, 5, 10, model->header); | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontKeyboard); | ||||||
|  | 
 | ||||||
|  |     if(model->selected_row == -1) { | ||||||
|  |         byte_input_draw_input_selected(canvas, model); | ||||||
|  |     } else { | ||||||
|  |         byte_input_draw_input(canvas, model); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(uint8_t row = 0; row < keyboard_row_count; row++) { | ||||||
|  |         const uint8_t column_count = byte_input_get_row_size(row); | ||||||
|  |         const ByteInputKey* keys = byte_input_get_row(row); | ||||||
|  | 
 | ||||||
|  |         for(size_t column = 0; column < column_count; column++) { | ||||||
|  |             if(keys[column].value == enter_symbol) { | ||||||
|  |                 canvas_set_color(canvas, ColorBlack); | ||||||
|  |                 if(model->selected_row == row && model->selected_column == column) { | ||||||
|  |                     canvas_draw_icon_name( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x, | ||||||
|  |                         keyboard_origin_y + keys[column].y, | ||||||
|  |                         I_KeySaveSelected_24x11); | ||||||
|  |                 } else { | ||||||
|  |                     canvas_draw_icon_name( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x, | ||||||
|  |                         keyboard_origin_y + keys[column].y, | ||||||
|  |                         I_KeySave_24x11); | ||||||
|  |                 } | ||||||
|  |             } else if(keys[column].value == backspace_symbol) { | ||||||
|  |                 canvas_set_color(canvas, ColorBlack); | ||||||
|  |                 if(model->selected_row == row && model->selected_column == column) { | ||||||
|  |                     canvas_draw_icon_name( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x, | ||||||
|  |                         keyboard_origin_y + keys[column].y, | ||||||
|  |                         I_KeyBackspaceSelected_16x9); | ||||||
|  |                 } else { | ||||||
|  |                     canvas_draw_icon_name( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x, | ||||||
|  |                         keyboard_origin_y + keys[column].y, | ||||||
|  |                         I_KeyBackspace_16x9); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 if(model->selected_row == row && model->selected_column == column) { | ||||||
|  |                     canvas_set_color(canvas, ColorBlack); | ||||||
|  |                     canvas_draw_box( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x - 3, | ||||||
|  |                         keyboard_origin_y + keys[column].y - 10, | ||||||
|  |                         11, | ||||||
|  |                         13); | ||||||
|  |                     canvas_set_color(canvas, ColorWhite); | ||||||
|  |                 } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) { | ||||||
|  |                     canvas_set_color(canvas, ColorBlack); | ||||||
|  |                     canvas_draw_frame( | ||||||
|  |                         canvas, | ||||||
|  |                         keyboard_origin_x + keys[column].x - 3, | ||||||
|  |                         keyboard_origin_y + keys[column].y - 10, | ||||||
|  |                         11, | ||||||
|  |                         13); | ||||||
|  |                 } else { | ||||||
|  |                     canvas_set_color(canvas, ColorBlack); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 canvas_draw_glyph( | ||||||
|  |                     canvas, | ||||||
|  |                     keyboard_origin_x + keys[column].x, | ||||||
|  |                     keyboard_origin_y + keys[column].y, | ||||||
|  |                     keys[column].value); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Input callback | ||||||
|  |  *  | ||||||
|  |  * @param event  | ||||||
|  |  * @param context  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | static bool byte_input_view_input_callback(InputEvent* event, void* context) { | ||||||
|  |     ByteInput* byte_input = context; | ||||||
|  |     furi_assert(byte_input); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         switch(event->key) { | ||||||
|  |         case InputKeyLeft: | ||||||
|  |             with_view_model( | ||||||
|  |                 byte_input->view, (ByteInputModel * model) { | ||||||
|  |                     byte_input_handle_left(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case InputKeyRight: | ||||||
|  |             with_view_model( | ||||||
|  |                 byte_input->view, (ByteInputModel * model) { | ||||||
|  |                     byte_input_handle_right(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case InputKeyUp: | ||||||
|  |             with_view_model( | ||||||
|  |                 byte_input->view, (ByteInputModel * model) { | ||||||
|  |                     byte_input_handle_up(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case InputKeyDown: | ||||||
|  |             with_view_model( | ||||||
|  |                 byte_input->view, (ByteInputModel * model) { | ||||||
|  |                     byte_input_handle_down(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case InputKeyOk: | ||||||
|  |             with_view_model( | ||||||
|  |                 byte_input->view, (ByteInputModel * model) { | ||||||
|  |                     byte_input_handle_ok(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Reset all input-related data in model | ||||||
|  |  *  | ||||||
|  |  * @param model ByteInputModel | ||||||
|  |  */ | ||||||
|  | static void byte_input_reset_model_input_data(ByteInputModel* model) { | ||||||
|  |     model->bytes = NULL; | ||||||
|  |     model->bytes_count = 0; | ||||||
|  |     model->selected_high_nibble = true; | ||||||
|  |     model->selected_byte = 0; | ||||||
|  |     model->selected_row = 0; | ||||||
|  |     model->selected_column = 0; | ||||||
|  |     model->first_visible_byte = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Allocate and initialize byte input. This byte input is used to enter bytes. | ||||||
|  |  *  | ||||||
|  |  * @return ByteInput instance pointer | ||||||
|  |  */ | ||||||
|  | ByteInput* byte_input_alloc() { | ||||||
|  |     ByteInput* byte_input = furi_alloc(sizeof(ByteInput)); | ||||||
|  |     byte_input->view = view_alloc(); | ||||||
|  |     view_set_context(byte_input->view, byte_input); | ||||||
|  |     view_allocate_model(byte_input->view, ViewModelTypeLocking, sizeof(ByteInputModel)); | ||||||
|  |     view_set_draw_callback(byte_input->view, byte_input_view_draw_callback); | ||||||
|  |     view_set_input_callback(byte_input->view, byte_input_view_input_callback); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         byte_input->view, (ByteInputModel * model) { | ||||||
|  |             model->header = ""; | ||||||
|  |             model->input_callback = NULL; | ||||||
|  |             model->changed_callback = NULL; | ||||||
|  |             model->callback_context = NULL; | ||||||
|  |             byte_input_reset_model_input_data(model); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return byte_input; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Deinitialize and free byte input | ||||||
|  |  *  | ||||||
|  |  * @param byte_input Byte input instance | ||||||
|  |  */ | ||||||
|  | void byte_input_free(ByteInput* byte_input) { | ||||||
|  |     furi_assert(byte_input); | ||||||
|  |     view_free(byte_input->view); | ||||||
|  |     free(byte_input); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Get byte input view | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @return View instance that can be used for embedding | ||||||
|  |  */ | ||||||
|  | View* byte_input_get_view(ByteInput* byte_input) { | ||||||
|  |     furi_assert(byte_input); | ||||||
|  |     return byte_input->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Deinitialize and free byte input | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @param input_callback input callback fn | ||||||
|  |  * @param changed_callback changed callback fn | ||||||
|  |  * @param callback_context callback context | ||||||
|  |  * @param bytes buffer to use | ||||||
|  |  * @param bytes_count buffer length | ||||||
|  |  */ | ||||||
|  | void byte_input_set_result_callback( | ||||||
|  |     ByteInput* byte_input, | ||||||
|  |     ByteInputCallback input_callback, | ||||||
|  |     ByteChangedCallback changed_callback, | ||||||
|  |     void* callback_context, | ||||||
|  |     uint8_t* bytes, | ||||||
|  |     uint8_t bytes_count) { | ||||||
|  |     with_view_model( | ||||||
|  |         byte_input->view, (ByteInputModel * model) { | ||||||
|  |             byte_input_reset_model_input_data(model); | ||||||
|  |             model->input_callback = input_callback; | ||||||
|  |             model->changed_callback = changed_callback; | ||||||
|  |             model->callback_context = callback_context; | ||||||
|  |             model->bytes = bytes; | ||||||
|  |             model->bytes_count = bytes_count; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set byte input header text | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @param text text to be shown | ||||||
|  |  */ | ||||||
|  | void byte_input_set_header_text(ByteInput* byte_input, const char* text) { | ||||||
|  |     with_view_model( | ||||||
|  |         byte_input->view, (ByteInputModel * model) { | ||||||
|  |             model->header = text; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								applications/gui/modules/byte_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								applications/gui/modules/byte_input.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Byte input anonymous structure  | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | typedef struct ByteInput ByteInput; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief callback that is executed on save button press | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | typedef void (*ByteInputCallback)(void* context, uint8_t* bytes, uint8_t bytes_count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief callback that is executed when byte buffer is changed | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | typedef void (*ByteChangedCallback)(void* context, uint8_t* bytes, uint8_t bytes_count); | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Allocate and initialize byte input. This byte input is used to enter bytes. | ||||||
|  |  *  | ||||||
|  |  * @return ByteInput instance pointer | ||||||
|  |  */ | ||||||
|  | ByteInput* byte_input_alloc(); | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Deinitialize and free byte input | ||||||
|  |  *  | ||||||
|  |  * @param byte_input Byte input instance | ||||||
|  |  */ | ||||||
|  | void byte_input_free(ByteInput* byte_input); | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Get byte input view | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @return View instance that can be used for embedding | ||||||
|  |  */ | ||||||
|  | View* byte_input_get_view(ByteInput* byte_input); | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Deinitialize and free byte input | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @param input_callback input callback fn | ||||||
|  |  * @param changed_callback changed callback fn | ||||||
|  |  * @param callback_context callback context | ||||||
|  |  * @param bytes buffer to use | ||||||
|  |  * @param bytes_count buffer length | ||||||
|  |  */ | ||||||
|  | void byte_input_set_result_callback( | ||||||
|  |     ByteInput* byte_input, | ||||||
|  |     ByteInputCallback input_callback, | ||||||
|  |     ByteChangedCallback changed_callback, | ||||||
|  |     void* callback_context, | ||||||
|  |     uint8_t* bytes, | ||||||
|  |     uint8_t bytes_count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set byte input header text | ||||||
|  |  *  | ||||||
|  |  * @param byte_input byte input instance | ||||||
|  |  * @param text text to be shown | ||||||
|  |  */ | ||||||
|  | void byte_input_set_header_text(ByteInput* byte_input, const char* text); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,7 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /* Dialog anonymous structure */ | /* Dialog anonymous structure */ | ||||||
| typedef struct Dialog Dialog; | typedef struct Dialog Dialog; | ||||||
| 
 | 
 | ||||||
| @ -67,3 +70,7 @@ void dialog_set_left_button_text(Dialog* dialog, const char* text); | |||||||
|  * @param text - text to be shown |  * @param text - text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_set_right_button_text(Dialog* dialog, const char* text); | void dialog_set_right_button_text(Dialog* dialog, const char* text); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,7 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /* Dialog anonymous structure */ | /* Dialog anonymous structure */ | ||||||
| typedef struct DialogEx DialogEx; | typedef struct DialogEx DialogEx; | ||||||
| 
 | 
 | ||||||
| @ -103,3 +106,7 @@ void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text); | |||||||
|  * @param text - text to be shown |  * @param text - text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text); | void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										358
									
								
								applications/gui/modules/file_select.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								applications/gui/modules/file_select.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | |||||||
|  | #include "file_select.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
|  | #define FILENAME_COUNT 4 | ||||||
|  | 
 | ||||||
|  | struct FileSelect { | ||||||
|  |     // public
 | ||||||
|  |     View* view; | ||||||
|  |     FS_Api* fs_api; | ||||||
|  |     char* path; | ||||||
|  |     char* extension; | ||||||
|  | 
 | ||||||
|  |     bool init_completed; | ||||||
|  | 
 | ||||||
|  |     FileSelectCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     string_t filename[FILENAME_COUNT]; | ||||||
|  |     uint8_t position; | ||||||
|  | 
 | ||||||
|  |     uint16_t first_file_index; | ||||||
|  |     uint16_t file_count; | ||||||
|  | 
 | ||||||
|  | } FileSelectModel; | ||||||
|  | 
 | ||||||
|  | bool file_select_fill_strings(FileSelect* file_select); | ||||||
|  | bool file_select_fill_count(FileSelect* file_select); | ||||||
|  | static bool file_select_init(FileSelect* file_select); | ||||||
|  | 
 | ||||||
|  | static void file_select_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     FileSelectModel* model = _model; | ||||||
|  | 
 | ||||||
|  |     const uint8_t item_height = 16; | ||||||
|  |     const uint8_t item_width = 123; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < FILENAME_COUNT; i++) { | ||||||
|  |         if(i == model->position) { | ||||||
|  |             canvas_set_color(canvas, ColorBlack); | ||||||
|  |             canvas_draw_box(canvas, 0, (i * item_height) + 1, item_width, item_height - 2); | ||||||
|  | 
 | ||||||
|  |             canvas_set_color(canvas, ColorWhite); | ||||||
|  |             canvas_draw_dot(canvas, 0, (i * item_height) + 1); | ||||||
|  |             canvas_draw_dot(canvas, 0, (i * item_height) + item_height - 2); | ||||||
|  |             canvas_draw_dot(canvas, item_width - 1, (i * item_height) + 1); | ||||||
|  |             canvas_draw_dot(canvas, item_width - 1, (i * item_height) + item_height - 2); | ||||||
|  |         } else { | ||||||
|  |             canvas_set_color(canvas, ColorBlack); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         canvas_draw_str( | ||||||
|  |             canvas, 6, (i * item_height) + item_height - 4, string_get_cstr(model->filename[i])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     elements_scrollbar(canvas, model->first_file_index + model->position, model->file_count); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool file_select_input_callback(InputEvent* event, void* context) { | ||||||
|  |     FileSelect* file_select = (FileSelect*)context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         if(!file_select->init_completed) { | ||||||
|  |             if(!file_select_init(file_select)) { | ||||||
|  |                 file_select->callback(NULL, file_select->context); | ||||||
|  |             } | ||||||
|  |         } else if(event->key == InputKeyUp) { | ||||||
|  |             with_view_model( | ||||||
|  |                 file_select->view, (FileSelectModel * model) { | ||||||
|  |                     if(model->position == 0) { | ||||||
|  |                         if(model->first_file_index == 0) { | ||||||
|  |                             // wrap
 | ||||||
|  |                             uint16_t max_first_file_index = model->file_count - FILENAME_COUNT; | ||||||
|  |                             model->position = FILENAME_COUNT - 1; | ||||||
|  |                             model->first_file_index = max_first_file_index; | ||||||
|  |                         } else { | ||||||
|  |                             model->first_file_index--; | ||||||
|  |                         } | ||||||
|  |                     } else if(model->position == 1) { | ||||||
|  |                         if(model->first_file_index == 0) { | ||||||
|  |                             model->position--; | ||||||
|  |                         } else { | ||||||
|  |                             model->first_file_index--; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         model->position--; | ||||||
|  |                     } | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event->key == InputKeyDown) { | ||||||
|  |             with_view_model( | ||||||
|  |                 file_select->view, (FileSelectModel * model) { | ||||||
|  |                     uint16_t max_first_file_index = model->file_count - FILENAME_COUNT; | ||||||
|  | 
 | ||||||
|  |                     if(model->position >= (FILENAME_COUNT - 1)) { | ||||||
|  |                         if(model->first_file_index >= max_first_file_index) { | ||||||
|  |                             // wrap
 | ||||||
|  |                             model->position = 0; | ||||||
|  |                             model->first_file_index = 0; | ||||||
|  |                         } else { | ||||||
|  |                             model->first_file_index++; | ||||||
|  |                         } | ||||||
|  |                     } else if(model->position >= (FILENAME_COUNT - 2)) { | ||||||
|  |                         if(model->first_file_index >= max_first_file_index) { | ||||||
|  |                             model->position++; | ||||||
|  |                         } else { | ||||||
|  |                             model->first_file_index++; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         model->position++; | ||||||
|  |                     } | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event->key == InputKeyOk) { | ||||||
|  |             if(file_select->callback != NULL) { | ||||||
|  |                 const char* result; | ||||||
|  |                 with_view_model( | ||||||
|  |                     file_select->view, (FileSelectModel * model) { | ||||||
|  |                         result = string_get_cstr(model->filename[model->position]); | ||||||
|  |                         return false; | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                 file_select->callback(result, file_select->context); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!file_select_fill_strings(file_select)) { | ||||||
|  |             file_select->callback(NULL, file_select->context); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool file_select_init(FileSelect* file_select) { | ||||||
|  |     bool result = false; | ||||||
|  |     if(file_select->path && file_select->extension && file_select->fs_api) { | ||||||
|  |         if(file_select_fill_count(file_select)) { | ||||||
|  |             if(file_select_fill_strings(file_select)) { | ||||||
|  |                 file_select->init_completed = true; | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FileSelect* file_select_alloc() { | ||||||
|  |     FileSelect* file_select = furi_alloc(sizeof(FileSelect)); | ||||||
|  |     file_select->view = view_alloc(); | ||||||
|  |     view_set_context(file_select->view, file_select); | ||||||
|  |     view_allocate_model(file_select->view, ViewModelTypeLockFree, sizeof(FileSelectModel)); | ||||||
|  |     view_set_draw_callback(file_select->view, file_select_draw_callback); | ||||||
|  |     view_set_input_callback(file_select->view, file_select_input_callback); | ||||||
|  | 
 | ||||||
|  |     file_select->fs_api = NULL; | ||||||
|  |     file_select->path = NULL; | ||||||
|  |     file_select->extension = NULL; | ||||||
|  |     file_select->init_completed = false; | ||||||
|  |     file_select->callback = NULL; | ||||||
|  |     file_select->context = NULL; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         file_select->view, (FileSelectModel * model) { | ||||||
|  |             for(uint8_t i = 0; i < FILENAME_COUNT; i++) { | ||||||
|  |                 string_init(model->filename[i]); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             model->first_file_index = 0; | ||||||
|  |             model->file_count = 0; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return file_select; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void file_select_free(FileSelect* file_select) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     with_view_model( | ||||||
|  |         file_select->view, (FileSelectModel * model) { | ||||||
|  |             for(uint8_t i = 0; i < FILENAME_COUNT; i++) { | ||||||
|  |                 string_clear(model->filename[i]); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  |     view_free(file_select->view); | ||||||
|  |     free(file_select); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* file_select_get_view(FileSelect* file_select) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     return file_select->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void file_select_set_api(FileSelect* file_select, FS_Api* fs_api) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     file_select->fs_api = fs_api; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void file_select_set_filter(FileSelect* file_select, char* path, char* extension) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     file_select->path = path; | ||||||
|  |     file_select->extension = extension; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(!(file_info->flags & FSF_DIRECTORY)) { | ||||||
|  |         if(strcmp(file_select->extension, "*") == 0) { | ||||||
|  |             result = true; | ||||||
|  |         } else if(strstr(name, file_select->extension) != NULL) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool file_select_fill_strings(FileSelect* file_select) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     furi_assert(file_select->fs_api); | ||||||
|  |     furi_assert(file_select->path); | ||||||
|  |     furi_assert(file_select->extension); | ||||||
|  | 
 | ||||||
|  |     FileInfo file_info; | ||||||
|  |     File directory; | ||||||
|  |     bool result; | ||||||
|  |     FS_Dir_Api* dir_api = &file_select->fs_api->dir; | ||||||
|  |     uint8_t string_counter = 0; | ||||||
|  |     uint16_t file_counter = 0; | ||||||
|  |     const uint8_t name_length = 100; | ||||||
|  |     char* name = calloc(name_length, sizeof(char)); | ||||||
|  |     uint16_t first_file_index = 0; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         file_select->view, (FileSelectModel * model) { | ||||||
|  |             first_file_index = model->first_file_index; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     if(name == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = dir_api->open(&directory, file_select->path); | ||||||
|  | 
 | ||||||
|  |     if(!result) { | ||||||
|  |         dir_api->close(&directory); | ||||||
|  |         free(name); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         result = dir_api->read(&directory, &file_info, name, name_length); | ||||||
|  | 
 | ||||||
|  |         if(directory.error_id == FSE_NOT_EXIST || name[0] == 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(result) { | ||||||
|  |             if(directory.error_id == FSE_OK) { | ||||||
|  |                 if(filter_file(file_select, &file_info, name)) { | ||||||
|  |                     if(file_counter >= first_file_index) { | ||||||
|  |                         with_view_model( | ||||||
|  |                             file_select->view, (FileSelectModel * model) { | ||||||
|  |                                 string_set_str(model->filename[string_counter], name); | ||||||
|  |                                 return true; | ||||||
|  |                             }); | ||||||
|  |                         string_counter++; | ||||||
|  | 
 | ||||||
|  |                         if(string_counter >= FILENAME_COUNT) { | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     file_counter++; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 dir_api->close(&directory); | ||||||
|  |                 free(name); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dir_api->close(&directory); | ||||||
|  |     free(name); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool file_select_fill_count(FileSelect* file_select) { | ||||||
|  |     furi_assert(file_select); | ||||||
|  |     furi_assert(file_select->fs_api); | ||||||
|  |     furi_assert(file_select->path); | ||||||
|  |     furi_assert(file_select->extension); | ||||||
|  | 
 | ||||||
|  |     FileInfo file_info; | ||||||
|  |     File directory; | ||||||
|  |     bool result; | ||||||
|  |     FS_Dir_Api* dir_api = &file_select->fs_api->dir; | ||||||
|  |     uint16_t file_counter = 0; | ||||||
|  |     const uint8_t name_length = 100; | ||||||
|  |     char* name = calloc(name_length, sizeof(char)); | ||||||
|  | 
 | ||||||
|  |     if(name == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = dir_api->open(&directory, file_select->path); | ||||||
|  | 
 | ||||||
|  |     if(!result) { | ||||||
|  |         dir_api->close(&directory); | ||||||
|  |         free(name); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         result = dir_api->read(&directory, &file_info, name, name_length); | ||||||
|  | 
 | ||||||
|  |         if(directory.error_id == FSE_NOT_EXIST || name[0] == 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(result) { | ||||||
|  |             if(directory.error_id == FSE_OK) { | ||||||
|  |                 if(filter_file(file_select, &file_info, name)) { | ||||||
|  |                     file_counter++; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 dir_api->close(&directory); | ||||||
|  |                 free(name); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         file_select->view, (FileSelectModel * model) { | ||||||
|  |             model->file_count = file_counter; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     dir_api->close(&directory); | ||||||
|  |     free(name); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								applications/gui/modules/file_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								applications/gui/modules/file_select.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <filesystem-api.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct FileSelect FileSelect; | ||||||
|  | 
 | ||||||
|  | typedef void (*FileSelectCallback)(const char* result, void* context); | ||||||
|  | 
 | ||||||
|  | FileSelect* file_select_alloc(); | ||||||
|  | 
 | ||||||
|  | void file_select_free(FileSelect* file_select); | ||||||
|  | View* file_select_get_view(FileSelect* file_select); | ||||||
|  | 
 | ||||||
|  | void file_select_set_api(FileSelect* file_select, FS_Api* fs_api); | ||||||
|  | void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); | ||||||
|  | void file_select_set_filter(FileSelect* file_select, char* path, char* extension); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,7 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /* Popup anonymous structure */ | /* Popup anonymous structure */ | ||||||
| typedef struct Popup Popup; | typedef struct Popup Popup; | ||||||
| 
 | 
 | ||||||
| @ -69,6 +72,7 @@ void popup_set_text( | |||||||
|     Align vertical); |     Align vertical); | ||||||
| 
 | 
 | ||||||
| /* Set popup icon
 | /* Set popup icon
 | ||||||
|  |  * If icon position is negative, popup icon will not be rendered | ||||||
|  * @param popup - Popup instance |  * @param popup - Popup instance | ||||||
|  * @param x, y - icon position |  * @param x, y - icon position | ||||||
|  * @param name - icon to be shown |  * @param name - icon to be shown | ||||||
| @ -89,4 +93,8 @@ void popup_enable_timeout(Popup* popup); | |||||||
| /* Disable popup timeout
 | /* Disable popup timeout
 | ||||||
|  * @param popup - Popup instance |  * @param popup - Popup instance | ||||||
|  */ |  */ | ||||||
| void popup_disable_timeout(Popup* popup); | void popup_disable_timeout(Popup* popup); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "text_input.h" | #include "text_input.h" | ||||||
|  | #include <gui/elements.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| struct TextInput { | struct TextInput { | ||||||
| @ -136,10 +137,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     canvas_draw_str(canvas, 2, 8, model->header); |     canvas_draw_str(canvas, 2, 8, model->header); | ||||||
|     canvas_draw_line(canvas, 2, 12, canvas_width(canvas) - 7, 12); |     elements_slightly_rounded_frame(canvas, 1, 12, 122, 15); | ||||||
|     canvas_draw_line(canvas, 1, 13, 1, 25); |  | ||||||
|     canvas_draw_line(canvas, canvas_width(canvas) - 6, 13, canvas_width(canvas) - 6, 25); |  | ||||||
|     canvas_draw_line(canvas, 2, 26, canvas_width(canvas) - 7, 26); |  | ||||||
| 
 | 
 | ||||||
|     while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { |     while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { | ||||||
|         text++; |         text++; | ||||||
| @ -151,7 +149,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     canvas_set_font(canvas, FontKeyboard); |     canvas_set_font(canvas, FontKeyboard); | ||||||
| 
 | 
 | ||||||
|     for(uint8_t row = 0; row <= keyboard_row_count; row++) { |     for(uint8_t row = 0; row <= keyboard_row_count; row++) { | ||||||
|         uint8_t volatile column_count = get_row_size(row); |         const uint8_t column_count = get_row_size(row); | ||||||
|         const TextInputKey* keys = get_row(row); |         const TextInputKey* keys = get_row(row); | ||||||
| 
 | 
 | ||||||
|         for(size_t column = 0; column < column_count; column++) { |         for(size_t column = 0; column < column_count; column++) { | ||||||
|  | |||||||
| @ -1,6 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /* Text input anonymous structure */ | /* Text input anonymous structure */ | ||||||
| typedef struct TextInput TextInput; | typedef struct TextInput TextInput; | ||||||
| typedef void (*TextInputCallback)(void* context, char* text); | typedef void (*TextInputCallback)(void* context, char* text); | ||||||
| @ -39,4 +43,8 @@ void text_input_set_result_callback( | |||||||
|  * @param text input - Text input instance |  * @param text input - Text input instance | ||||||
|  * @param text - text to be shown |  * @param text - text to be shown | ||||||
|  */ |  */ | ||||||
| void text_input_set_header_text(TextInput* text_input, const char* text); | void text_input_set_header_text(TextInput* text_input, const char* text); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -37,7 +37,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { | |||||||
| void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) { | void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) { | ||||||
|     furi_assert(view_dispatcher); |     furi_assert(view_dispatcher); | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     // Check if view id is not used and resgister view
 |     // Check if view id is not used and register view
 | ||||||
|     furi_check(ViewDict_get(view_dispatcher->views, view_id) == NULL); |     furi_check(ViewDict_get(view_dispatcher->views, view_id) == NULL); | ||||||
| 
 | 
 | ||||||
|     // Lock gui
 |     // Lock gui
 | ||||||
|  | |||||||
							
								
								
									
										193
									
								
								applications/ibutton/helpers/cyfral-decoder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								applications/ibutton/helpers/cyfral-decoder.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | |||||||
|  | #include "cyfral-decoder.h" | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | void CyfralDecoder::reset_state() { | ||||||
|  |     state = State::WAIT_START_NIBBLE; | ||||||
|  |     bit_state = BitState::WAIT_FRONT_LOW; | ||||||
|  | 
 | ||||||
|  |     period_time = 0; | ||||||
|  |     bit_index = 0; | ||||||
|  |     ready = false; | ||||||
|  |     index = 0; | ||||||
|  | 
 | ||||||
|  |     key_data = 0; | ||||||
|  |     readed_nibble = 0; | ||||||
|  |     data_valid = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralDecoder::nibble_valid(uint8_t data) { | ||||||
|  |     uint8_t data_value = data & 0x0F; | ||||||
|  | 
 | ||||||
|  |     switch(data_value) { | ||||||
|  |     case 0b1110: | ||||||
|  |     case 0b1101: | ||||||
|  |     case 0b1011: | ||||||
|  |     case 0b0111: | ||||||
|  |         return true; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralDecoder::CyfralDecoder() { | ||||||
|  |     reset_state(); | ||||||
|  |     max_period = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralDecoder::process_front(bool polarity, uint32_t time) { | ||||||
|  |     bool readed; | ||||||
|  |     bool value; | ||||||
|  | 
 | ||||||
|  |     if(max_period == 0) { | ||||||
|  |         max_period = 230 * (SystemCoreClock / 1000000.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(ready) return; | ||||||
|  | 
 | ||||||
|  |     switch(state) { | ||||||
|  |     case State::WAIT_START_NIBBLE: | ||||||
|  |         // wait for start word
 | ||||||
|  |         if(process_bit(polarity, time, &readed, &value)) { | ||||||
|  |             if(readed) { | ||||||
|  |                 readed_nibble = ((readed_nibble << 1) | value) & 0x0F; | ||||||
|  |                 if(readed_nibble == 0b0001) { | ||||||
|  |                     readed_nibble = 0; | ||||||
|  |                     state = State::READ_NIBBLE; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             reset_state(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case State::READ_NIBBLE: | ||||||
|  |         // read nibbles
 | ||||||
|  |         if(process_bit(polarity, time, &readed, &value)) { | ||||||
|  |             if(readed) { | ||||||
|  |                 readed_nibble = (readed_nibble << 1) | value; | ||||||
|  | 
 | ||||||
|  |                 bit_index++; | ||||||
|  | 
 | ||||||
|  |                 //convert every nibble to 2-bit index
 | ||||||
|  |                 if(bit_index == 4) { | ||||||
|  |                     switch(readed_nibble) { | ||||||
|  |                     case 0b1110: | ||||||
|  |                         key_data = (key_data << 2) | 0b11; | ||||||
|  |                         break; | ||||||
|  |                     case 0b1101: | ||||||
|  |                         key_data = (key_data << 2) | 0b10; | ||||||
|  |                         break; | ||||||
|  |                     case 0b1011: | ||||||
|  |                         key_data = (key_data << 2) | 0b01; | ||||||
|  |                         break; | ||||||
|  |                     case 0b0111: | ||||||
|  |                         key_data = (key_data << 2) | 0b00; | ||||||
|  |                         break; | ||||||
|  |                     default: | ||||||
|  |                         data_valid = false; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     readed_nibble = 0; | ||||||
|  |                     bit_index = 0; | ||||||
|  |                     index++; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // succefully read 8 nibbles
 | ||||||
|  |                 if(index == 8) { | ||||||
|  |                     state = State::READ_STOP_NIBBLE; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             reset_state(); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case State::READ_STOP_NIBBLE: | ||||||
|  |         // read stop nibble
 | ||||||
|  |         if(process_bit(polarity, time, &readed, &value)) { | ||||||
|  |             if(readed) { | ||||||
|  |                 readed_nibble = ((readed_nibble << 1) | value) & 0x0F; | ||||||
|  |                 bit_index++; | ||||||
|  | 
 | ||||||
|  |                 switch(bit_index) { | ||||||
|  |                 case 0: | ||||||
|  |                 case 1: | ||||||
|  |                 case 2: | ||||||
|  |                 case 3: | ||||||
|  |                     break; | ||||||
|  |                 case 4: | ||||||
|  |                     if(readed_nibble == 0b0001) { | ||||||
|  |                         // validate data
 | ||||||
|  |                         if(data_valid) { | ||||||
|  |                             ready = true; | ||||||
|  |                         } else { | ||||||
|  |                             reset_state(); | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         reset_state(); | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     reset_state(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             reset_state(); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralDecoder::process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value) { | ||||||
|  |     bool result = true; | ||||||
|  |     *readed = false; | ||||||
|  | 
 | ||||||
|  |     // bit start from low
 | ||||||
|  |     switch(bit_state) { | ||||||
|  |     case BitState::WAIT_FRONT_LOW: | ||||||
|  |         if(polarity == true) { | ||||||
|  |             period_time += time; | ||||||
|  | 
 | ||||||
|  |             *readed = true; | ||||||
|  |             if(period_time <= max_period) { | ||||||
|  |                 if((period_time / 2) > time) { | ||||||
|  |                     *readed_value = false; | ||||||
|  |                 } else { | ||||||
|  |                     *readed_value = true; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 result = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bit_state = BitState::WAIT_FRONT_HIGH; | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case BitState::WAIT_FRONT_HIGH: | ||||||
|  |         if(polarity == false) { | ||||||
|  |             period_time = time; | ||||||
|  |             bit_state = BitState::WAIT_FRONT_LOW; | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralDecoder::read(uint8_t* _data, uint8_t data_size) { | ||||||
|  |     furi_check(data_size <= 2); | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(ready) { | ||||||
|  |         memcpy(_data, &key_data, data_size); | ||||||
|  |         reset_state(); | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								applications/ibutton/helpers/cyfral-decoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/ibutton/helpers/cyfral-decoder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | class CyfralDecoder { | ||||||
|  | public: | ||||||
|  |     bool read(uint8_t* data, uint8_t data_size); | ||||||
|  |     void process_front(bool polarity, uint32_t time); | ||||||
|  | 
 | ||||||
|  |     CyfralDecoder(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     enum class BitState : uint8_t { | ||||||
|  |         WAIT_FRONT_HIGH, | ||||||
|  |         WAIT_FRONT_LOW, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     enum class State : uint8_t { | ||||||
|  |         WAIT_START_NIBBLE, | ||||||
|  |         READ_NIBBLE, | ||||||
|  |         READ_STOP_NIBBLE, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     State state; | ||||||
|  |     BitState bit_state; | ||||||
|  | 
 | ||||||
|  |     bool process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value); | ||||||
|  |     void reset_state(); | ||||||
|  |     bool nibble_valid(uint8_t data); | ||||||
|  | 
 | ||||||
|  |     // high + low period time
 | ||||||
|  |     uint32_t period_time; | ||||||
|  | 
 | ||||||
|  |     // ready flag, key is readed and valid
 | ||||||
|  |     std::atomic<bool> ready; | ||||||
|  | 
 | ||||||
|  |     // key data storage
 | ||||||
|  |     uint16_t key_data; | ||||||
|  | 
 | ||||||
|  |     // temporary nibble storage
 | ||||||
|  |     uint8_t readed_nibble; | ||||||
|  | 
 | ||||||
|  |     // data valid flag
 | ||||||
|  |     // MUST be checked only in READ_STOP_NIBBLE state
 | ||||||
|  |     bool data_valid; | ||||||
|  | 
 | ||||||
|  |     // nibble index, we expect 8 nibbles
 | ||||||
|  |     uint8_t index; | ||||||
|  | 
 | ||||||
|  |     // bit index in nibble, 4 bit per nibble
 | ||||||
|  |     uint8_t bit_index; | ||||||
|  | 
 | ||||||
|  |     // max period, 230us x clock per us
 | ||||||
|  |     uint32_t max_period; | ||||||
|  | }; | ||||||
							
								
								
									
										39
									
								
								applications/ibutton/helpers/key-commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								applications/ibutton/helpers/key-commands.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | class RW1990_1 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1; | ||||||
|  |     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RW1990_2 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D; | ||||||
|  |     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class TM2004 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_READ_STATUS = 0xAA; | ||||||
|  |     constexpr static const uint8_t CMD_READ_MEMORY = 0xF0; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0x3C; | ||||||
|  |     constexpr static const uint8_t CMD_FINALIZATION = 0x35; | ||||||
|  | 
 | ||||||
|  |     constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class TM01 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xC5; | ||||||
|  |     constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA; | ||||||
|  |     constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class DS1990 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_READ_ROM = 0x33; | ||||||
|  | }; | ||||||
							
								
								
									
										204
									
								
								applications/ibutton/helpers/key-emulator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								applications/ibutton/helpers/key-emulator.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,204 @@ | |||||||
|  | #include "key-emulator.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | KeyEmulator::~KeyEmulator() { | ||||||
|  |     onewire_slave->stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyEmulator::KeyEmulator(OneWireSlave* _onewire_slave) | ||||||
|  |     : dallas_key{0, 0, 0, 0, 0, 0, 0} { | ||||||
|  |     onewire_slave = _onewire_slave; | ||||||
|  | 
 | ||||||
|  |     auto cb = cbc::obtain_connector(this, &KeyEmulator::result_callback); | ||||||
|  |     onewire_slave->set_result_callback(cb, this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::start(iButtonKey* key) { | ||||||
|  |     anything_emulated = false; | ||||||
|  |     stop(); | ||||||
|  | 
 | ||||||
|  |     switch(key->get_key_type()) { | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         start_dallas_emulate(key); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         start_cyfral_emulate(key); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         start_metakom_emulate(key); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyEmulator::emulated() { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(anything_emulated) { | ||||||
|  |         anything_emulated = false; | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::stop() { | ||||||
|  |     onewire_slave->stop(); | ||||||
|  |     pulser.stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::start_cyfral_emulate(iButtonKey* key) { | ||||||
|  |     furi_assert(key->get_key_type() == iButtonKeyType::KeyCyfral); | ||||||
|  |     furi_assert(key->get_type_data_size() == 2); | ||||||
|  | 
 | ||||||
|  |     const uint32_t cyfral_period_full = 8000; | ||||||
|  |     const uint32_t cyfral_period_one[2] = { | ||||||
|  |         uint32_t(cyfral_period_full * 0.33f), uint32_t(cyfral_period_full * 0.66f)}; | ||||||
|  |     const uint32_t cyfral_period_zero[2] = { | ||||||
|  |         uint32_t(cyfral_period_full * 0.66f), uint32_t(cyfral_period_full * 0.33f)}; | ||||||
|  |     uint8_t pd_index = 0; | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     // start nibble
 | ||||||
|  |     set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |     pd_index++; | ||||||
|  |     set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |     pd_index++; | ||||||
|  |     set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |     pd_index++; | ||||||
|  |     set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |     pd_index++; | ||||||
|  | 
 | ||||||
|  |     // data nibbles x 8
 | ||||||
|  |     for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) { | ||||||
|  |         for(int8_t j = 3; j >= 0; j--) { | ||||||
|  |             switch((key_data[i] >> (j * 2)) & 0b00000011) { | ||||||
|  |             case 0b11: | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |                 pd_index++; | ||||||
|  |                 break; | ||||||
|  |             case 0b10: | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 break; | ||||||
|  |             case 0b01: | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 break; | ||||||
|  |             case 0b00: | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 set_pulse_data_cyfral(pd_index, cyfral_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 // cannot be anyway
 | ||||||
|  |                 furi_check(false); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 4 (nibbles) x (8 data + 1 start) = 4 x 9 = 36
 | ||||||
|  |     if(pd_index != 36) { | ||||||
|  |         // something is very wrong
 | ||||||
|  |         furi_check(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pulser.set_periods(pulse_data, 72, false); | ||||||
|  |     pulser.start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::start_metakom_emulate(iButtonKey* key) { | ||||||
|  |     furi_assert(key->get_key_type() == iButtonKeyType::KeyMetakom); | ||||||
|  |     furi_assert(key->get_type_data_size() == 4); | ||||||
|  | 
 | ||||||
|  |     const uint32_t metakom_period_full = 8000; | ||||||
|  |     const uint32_t metakom_period_zero[2] = { | ||||||
|  |         uint32_t(metakom_period_full * 0.33f), uint32_t(metakom_period_full * 0.66f)}; | ||||||
|  |     const uint32_t metakom_period_one[2] = { | ||||||
|  |         uint32_t(metakom_period_full * 0.66f), uint32_t(metakom_period_full * 0.33f)}; | ||||||
|  |     uint8_t pd_index = 0; | ||||||
|  | 
 | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     // start pulse
 | ||||||
|  |     pulse_data[0] = metakom_period_full * 4; | ||||||
|  | 
 | ||||||
|  |     // start triplet
 | ||||||
|  |     set_pulse_data_metakom(pd_index, metakom_period_zero); | ||||||
|  |     pd_index++; | ||||||
|  |     set_pulse_data_metakom(pd_index, metakom_period_one); | ||||||
|  |     pd_index++; | ||||||
|  |     set_pulse_data_metakom(pd_index, metakom_period_zero); | ||||||
|  |     pd_index++; | ||||||
|  | 
 | ||||||
|  |     for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) { | ||||||
|  |         for(int8_t j = 7; j >= 0; j--) { | ||||||
|  |             if(((key_data[i] >> j) & 0b00000001) == 1) { | ||||||
|  |                 set_pulse_data_metakom(pd_index, metakom_period_one); | ||||||
|  |                 pd_index++; | ||||||
|  |             } else { | ||||||
|  |                 set_pulse_data_metakom(pd_index, metakom_period_zero); | ||||||
|  |                 pd_index++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 4 byte x 8 bits + 3 start bits = 35
 | ||||||
|  |     if(pd_index != 35) { | ||||||
|  |         // something is very wrong
 | ||||||
|  |         furi_check(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pulser.set_periods(pulse_data, 71, false); | ||||||
|  |     pulser.start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::start_dallas_emulate(iButtonKey* key) { | ||||||
|  |     furi_assert(key->get_key_type() == iButtonKeyType::KeyDallas); | ||||||
|  |     furi_assert(key->get_type_data_size() == 8); | ||||||
|  | 
 | ||||||
|  |     onewire_slave->deattach(); | ||||||
|  |     memcpy(dallas_key.id_storage, key->get_data(), key->get_type_data_size()); | ||||||
|  |     onewire_slave->attach(&dallas_key); | ||||||
|  |     onewire_slave->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::set_pulse_data_cyfral(uint8_t index, const uint32_t* data) { | ||||||
|  |     pulse_data[index * 2] = data[0]; | ||||||
|  |     pulse_data[index * 2 + 1] = data[1]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::set_pulse_data_metakom(uint8_t index, const uint32_t* data) { | ||||||
|  |     // damn start pulse
 | ||||||
|  |     pulse_data[(index * 2) + 1] = data[0]; | ||||||
|  |     pulse_data[(index * 2) + 2] = data[1]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyEmulator::result_callback(bool success, void* ctx) { | ||||||
|  |     KeyEmulator* _this = static_cast<KeyEmulator*>(ctx); | ||||||
|  | 
 | ||||||
|  |     _this->anything_emulated = true; | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								applications/ibutton/helpers/key-emulator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/ibutton/helpers/key-emulator.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "pulse-sequencer.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <one_wire_slave.h> | ||||||
|  | #include <one_wire_device_ds_1990.h> | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | class KeyEmulator { | ||||||
|  | public: | ||||||
|  |     KeyEmulator(OneWireSlave* onewire_slave); | ||||||
|  |     ~KeyEmulator(); | ||||||
|  | 
 | ||||||
|  |     void start(iButtonKey* key); | ||||||
|  |     bool emulated(); | ||||||
|  |     void stop(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     DS1990 dallas_key; | ||||||
|  |     OneWireSlave* onewire_slave; | ||||||
|  | 
 | ||||||
|  |     PulseSequencer pulser; | ||||||
|  |     uint32_t pulse_data[72]; | ||||||
|  | 
 | ||||||
|  |     std::atomic<bool> anything_emulated; | ||||||
|  | 
 | ||||||
|  |     void start_cyfral_emulate(iButtonKey* key); | ||||||
|  |     void start_metakom_emulate(iButtonKey* key); | ||||||
|  |     void start_dallas_emulate(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  |     void set_pulse_data_cyfral(uint8_t index, const uint32_t* data); | ||||||
|  |     void set_pulse_data_metakom(uint8_t index, const uint32_t* data); | ||||||
|  | 
 | ||||||
|  |     void result_callback(bool success, void* ctx); | ||||||
|  | }; | ||||||
							
								
								
									
										10
									
								
								applications/ibutton/helpers/key-info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/ibutton/helpers/key-info.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | static const uint8_t IBUTTON_KEY_SIZE = 8; | ||||||
|  | 
 | ||||||
|  | enum class iButtonKeyType : uint8_t { | ||||||
|  |     KeyDallas, | ||||||
|  |     KeyCyfral, | ||||||
|  |     KeyMetakom, | ||||||
|  | }; | ||||||
							
								
								
									
										192
									
								
								applications/ibutton/helpers/key-reader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								applications/ibutton/helpers/key-reader.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | |||||||
|  | #include "key-reader.h" | ||||||
|  | #include "key-commands.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | #include <maxim_crc.h> | ||||||
|  | 
 | ||||||
|  | extern COMP_HandleTypeDef hcomp1; | ||||||
|  | 
 | ||||||
|  | KeyReader::Error KeyReader::read(iButtonKey* key) { | ||||||
|  |     uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||||
|  |     iButtonKeyType key_type; | ||||||
|  | 
 | ||||||
|  |     KeyReader::Error result = KeyReader::Error::EMPTY; | ||||||
|  | 
 | ||||||
|  |     if(read_key(&key_type, tmp_key_data, 8)) { | ||||||
|  |         switch(key_type) { | ||||||
|  |         case iButtonKeyType::KeyDallas: | ||||||
|  |             if(verify_key(key_type, tmp_key_data, 8)) { | ||||||
|  |                 if(maxim_crc8(tmp_key_data, 8) == 0) { | ||||||
|  |                     if(tmp_key_data[0] == 0x01) { | ||||||
|  |                         result = KeyReader::Error::OK; | ||||||
|  |                     } else { | ||||||
|  |                         result = KeyReader::Error::NOT_ARE_KEY; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     result = KeyReader::Error::CRC_ERROR; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyCyfral: | ||||||
|  |             result = KeyReader::Error::OK; | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyMetakom: | ||||||
|  |             result = KeyReader::Error::OK; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(result != KeyReader::Error::EMPTY) { | ||||||
|  |             key->set_type(key_type); | ||||||
|  |             key->set_data(tmp_key_data, 8); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch_mode_if_needed(); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyReader::KeyReader(OneWireMaster* _onewire_master) { | ||||||
|  |     onewire_master = _onewire_master; | ||||||
|  |     read_mode_switch_time = 0; | ||||||
|  |     read_mode = ReadMode::DALLAS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyReader::~KeyReader() { | ||||||
|  |     stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { | ||||||
|  |     bool readed = false; | ||||||
|  | 
 | ||||||
|  |     switch(read_mode) { | ||||||
|  |     case ReadMode::DALLAS: | ||||||
|  |         if(onewire_master->search(data)) { | ||||||
|  |             onewire_master->reset_search(); | ||||||
|  |             readed = true; | ||||||
|  |             *key_type = iButtonKeyType::KeyDallas; | ||||||
|  |         } else { | ||||||
|  |             onewire_master->reset_search(); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case ReadMode::CYFRAL_METAKOM: | ||||||
|  |         if(cyfral_decoder.read(data, 2)) { | ||||||
|  |             readed = true; | ||||||
|  |             *key_type = iButtonKeyType::KeyCyfral; | ||||||
|  |         } else if(metakom_decoder.read(data, 4)) { | ||||||
|  |             readed = true; | ||||||
|  |             *key_type = iButtonKeyType::KeyMetakom; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return readed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     switch(key_type) { | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         switch_to(ReadMode::DALLAS); | ||||||
|  | 
 | ||||||
|  |         if(onewire_master->reset()) { | ||||||
|  |             onewire_master->write(DS1990::CMD_READ_ROM); | ||||||
|  |             for(uint8_t i = 0; i < data_size; i++) { | ||||||
|  |                 if(onewire_master->read() != data[i]) { | ||||||
|  |                     result = false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         result = false; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::start_comaparator(void) { | ||||||
|  |     // pulldown lf-rfid pins to prevent interference
 | ||||||
|  |     // TODO open record
 | ||||||
|  |     GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||||
|  |     gpio_init(&rfid_pull_pin, GpioModeOutputOpenDrain); | ||||||
|  |     gpio_write(&rfid_pull_pin, false); | ||||||
|  | 
 | ||||||
|  |     // TODO open record
 | ||||||
|  |     GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||||
|  |     gpio_init(&rfid_out_pin, GpioModeOutputOpenDrain); | ||||||
|  |     gpio_write(&rfid_out_pin, false); | ||||||
|  | 
 | ||||||
|  |     comparator_callback_pointer = | ||||||
|  |         cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback); | ||||||
|  |     api_interrupt_add(comparator_callback_pointer, InterruptTypeComparatorTrigger, this); | ||||||
|  |     last_dwt_value = DWT->CYCCNT; | ||||||
|  |     HAL_COMP_Start(&hcomp1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::stop_comaparator(void) { | ||||||
|  |     HAL_COMP_Stop(&hcomp1); | ||||||
|  |     api_interrupt_remove(comparator_callback_pointer, InterruptTypeComparatorTrigger); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::comparator_trigger_callback(void* hcomp, void* comp_ctx) { | ||||||
|  |     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); | ||||||
|  |     KeyReader* _this = static_cast<KeyReader*>(comp_ctx); | ||||||
|  | 
 | ||||||
|  |     if(hcomp == &hcomp1) { | ||||||
|  |         _this->cyfral_decoder.process_front( | ||||||
|  |             (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH), | ||||||
|  |             DWT->CYCCNT - last_dwt_value); | ||||||
|  | 
 | ||||||
|  |         _this->metakom_decoder.process_front( | ||||||
|  |             (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH), | ||||||
|  |             DWT->CYCCNT - last_dwt_value); | ||||||
|  | 
 | ||||||
|  |         last_dwt_value = DWT->CYCCNT; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::switch_to(ReadMode mode) { | ||||||
|  |     switch(mode) { | ||||||
|  |     case ReadMode::DALLAS: | ||||||
|  |         onewire_master->start(); | ||||||
|  |         stop_comaparator(); | ||||||
|  |         break; | ||||||
|  |     case ReadMode::CYFRAL_METAKOM: | ||||||
|  |         onewire_master->stop(); | ||||||
|  |         start_comaparator(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     read_mode = mode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::switch_mode_if_needed() { | ||||||
|  |     if(osKernelGetTickCount() - read_mode_switch_time > (osKernelGetTickFreq() / 5)) { | ||||||
|  |         read_mode_switch_time = osKernelGetTickCount(); | ||||||
|  |         switch(read_mode) { | ||||||
|  |         case ReadMode::DALLAS: | ||||||
|  |             switch_to(ReadMode::CYFRAL_METAKOM); | ||||||
|  |             break; | ||||||
|  |         case ReadMode::CYFRAL_METAKOM: | ||||||
|  |             switch_to(ReadMode::DALLAS); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::start() { | ||||||
|  |     switch_to(ReadMode::CYFRAL_METAKOM); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyReader::stop() { | ||||||
|  |     onewire_master->stop(); | ||||||
|  |     stop_comaparator(); | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								applications/ibutton/helpers/key-reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/ibutton/helpers/key-reader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <furi.h> | ||||||
|  | #include "cyfral-decoder.h" | ||||||
|  | #pragma once | ||||||
|  | #include "metakom-decoder.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <one_wire_master.h> | ||||||
|  | #include <one_wire_slave.h> | ||||||
|  | 
 | ||||||
|  | class KeyReader { | ||||||
|  | public: | ||||||
|  |     enum class Error : uint8_t { | ||||||
|  |         EMPTY, | ||||||
|  |         CRC_ERROR, | ||||||
|  |         NOT_ARE_KEY, | ||||||
|  |         OK, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     void start(); | ||||||
|  |     void stop(); | ||||||
|  |     KeyReader::Error read(iButtonKey* key); | ||||||
|  |     KeyReader(OneWireMaster* onewire_master); | ||||||
|  |     ~KeyReader(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     bool read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size); | ||||||
|  |     bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size); | ||||||
|  | 
 | ||||||
|  |     // cyfral and metakom readers data
 | ||||||
|  |     void comparator_trigger_callback(void* hcomp, void* comp_ctx); | ||||||
|  |     void (*comparator_callback_pointer)(void* hcomp, void* comp_ctx); | ||||||
|  | 
 | ||||||
|  |     void start_comaparator(void); | ||||||
|  |     void stop_comaparator(void); | ||||||
|  |     uint32_t last_dwt_value; | ||||||
|  | 
 | ||||||
|  |     CyfralDecoder cyfral_decoder; | ||||||
|  |     MetakomDecoder metakom_decoder; | ||||||
|  | 
 | ||||||
|  |     // mode
 | ||||||
|  |     uint32_t read_mode_switch_time; | ||||||
|  |     enum class ReadMode : uint8_t { | ||||||
|  |         CYFRAL_METAKOM, | ||||||
|  |         DALLAS, | ||||||
|  |     }; | ||||||
|  |     ReadMode read_mode; | ||||||
|  | 
 | ||||||
|  |     // one wire
 | ||||||
|  |     OneWireMaster* onewire_master; | ||||||
|  | 
 | ||||||
|  |     void switch_to(ReadMode mode); | ||||||
|  |     void switch_mode_if_needed(); | ||||||
|  | }; | ||||||
							
								
								
									
										69
									
								
								applications/ibutton/helpers/key-store.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								applications/ibutton/helpers/key-store.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | #include "key-store.h" | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | uint16_t KeyStore::get_key_count() { | ||||||
|  |     return store.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t KeyStore::add_key() { | ||||||
|  |     store.push_back(iButtonKey()); | ||||||
|  |     return get_key_count() - 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyStore::set_key_type(uint8_t index, iButtonKeyType type) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     key->set_type(type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyStore::set_key_name(uint8_t index, char* name) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     char* orphan = strdup(name); | ||||||
|  |     key->set_name(orphan); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyStore::set_key_data(uint8_t index, uint8_t* data, uint8_t data_size) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     key->set_data(data, data_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKeyType KeyStore::get_key_type(uint8_t index) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     return key->get_key_type(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* KeyStore::get_key_name(uint8_t index) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     return key->get_name(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t* KeyStore::get_key_data(uint8_t index) { | ||||||
|  |     iButtonKey* key = get_key(index); | ||||||
|  |     return key->get_data(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyStore::remove_key(uint8_t index) { | ||||||
|  |     furi_check(index >= 0); | ||||||
|  |     furi_check(index < get_key_count()); | ||||||
|  |     auto item = std::next(store.begin(), index); | ||||||
|  |     store.erase(item); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyStore::KeyStore() { | ||||||
|  |     store.push_back(iButtonKey( | ||||||
|  |         iButtonKeyType::KeyDallas, "Dallas_1", 0x01, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6)); | ||||||
|  |     store.push_back(iButtonKey( | ||||||
|  |         iButtonKeyType::KeyDallas, "Dallas_2", 0x01, 0xFD, 0x0E, 0x84, 0x01, 0x00, 0x00, 0xDB)); | ||||||
|  |     store.push_back(iButtonKey( | ||||||
|  |         iButtonKeyType::KeyCyfral, "Cyfral_1", 0xA6, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); | ||||||
|  |     store.push_back(iButtonKey( | ||||||
|  |         iButtonKeyType::KeyMetakom, "Metakom_1", 0xB1, 0x2E, 0x47, 0xB2, 0x00, 0x00, 0x00, 0x00)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyStore::~KeyStore() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKey* KeyStore::get_key(uint8_t index) { | ||||||
|  |     furi_check(index >= 0); | ||||||
|  |     furi_check(index < get_key_count()); | ||||||
|  |     return &(*std::next(store.begin(), index)); | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								applications/ibutton/helpers/key-store.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/ibutton/helpers/key-store.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <list> | ||||||
|  | #include "key-info.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | 
 | ||||||
|  | class KeyStore { | ||||||
|  | public: | ||||||
|  |     uint16_t get_key_count(); | ||||||
|  | 
 | ||||||
|  |     uint8_t add_key(); | ||||||
|  | 
 | ||||||
|  |     void set_key_type(uint8_t index, iButtonKeyType type); | ||||||
|  |     void set_key_name(uint8_t index, char* name); | ||||||
|  |     void set_key_data(uint8_t index, uint8_t* data, uint8_t data_size); | ||||||
|  | 
 | ||||||
|  |     iButtonKeyType get_key_type(uint8_t index); | ||||||
|  |     const char* get_key_name(uint8_t index); | ||||||
|  |     uint8_t* get_key_data(uint8_t index); | ||||||
|  | 
 | ||||||
|  |     void remove_key(uint8_t index); | ||||||
|  | 
 | ||||||
|  |     KeyStore(); | ||||||
|  |     ~KeyStore(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::list<iButtonKey> store; | ||||||
|  |     iButtonKey* get_key(uint8_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										51
									
								
								applications/ibutton/helpers/key-worker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/ibutton/helpers/key-worker.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #include "key-worker.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | #include <maxim_crc.h> | ||||||
|  | 
 | ||||||
|  | extern COMP_HandleTypeDef hcomp1; | ||||||
|  | 
 | ||||||
|  | KeyReader::Error KeyWorker::read(iButtonKey* key) { | ||||||
|  |     KeyReader::Error result = key_reader.read(key); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::start_read() { | ||||||
|  |     key_reader.start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::stop_read() { | ||||||
|  |     key_reader.stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWorker::emulated() { | ||||||
|  |     return key_emulator.emulated(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::start_emulate(iButtonKey* key) { | ||||||
|  |     key_emulator.start(key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::stop_emulate() { | ||||||
|  |     key_emulator.stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyWriter::Error KeyWorker::write(iButtonKey* key) { | ||||||
|  |     return key_writer.write(key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::start_write() { | ||||||
|  |     key_writer.start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWorker::stop_write() { | ||||||
|  |     key_writer.stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyWorker::KeyWorker(const GpioPin* one_wire_gpio) | ||||||
|  |     : onewire_master{one_wire_gpio} | ||||||
|  |     , onewire_slave{one_wire_gpio} | ||||||
|  |     , key_reader{&onewire_master} | ||||||
|  |     , key_emulator{&onewire_slave} | ||||||
|  |     , key_writer{&onewire_master} { | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								applications/ibutton/helpers/key-worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/ibutton/helpers/key-worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <furi.h> | ||||||
|  | #include "key-info.h" | ||||||
|  | #include "key-reader.h" | ||||||
|  | #include "key-emulator.h" | ||||||
|  | #include "key-writer.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <one_wire_master.h> | ||||||
|  | #include <one_wire_slave.h> | ||||||
|  | 
 | ||||||
|  | class KeyWorker { | ||||||
|  | public: | ||||||
|  |     KeyReader::Error read(iButtonKey* key); | ||||||
|  |     void start_read(); | ||||||
|  |     void stop_read(); | ||||||
|  | 
 | ||||||
|  |     bool emulated(); | ||||||
|  |     void start_emulate(iButtonKey* key); | ||||||
|  |     void stop_emulate(); | ||||||
|  | 
 | ||||||
|  |     KeyWriter::Error write(iButtonKey* key); | ||||||
|  |     void start_write(); | ||||||
|  |     void stop_write(); | ||||||
|  | 
 | ||||||
|  |     KeyWorker(const GpioPin* one_wire_gpio); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     // one wire
 | ||||||
|  |     OneWireMaster onewire_master; | ||||||
|  |     OneWireSlave onewire_slave; | ||||||
|  |     KeyReader key_reader; | ||||||
|  |     KeyEmulator key_emulator; | ||||||
|  |     KeyWriter key_writer; | ||||||
|  | }; | ||||||
							
								
								
									
										272
									
								
								applications/ibutton/helpers/key-writer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								applications/ibutton/helpers/key-writer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,272 @@ | |||||||
|  | #include "key-writer.h" | ||||||
|  | #include "key-commands.h" | ||||||
|  | 
 | ||||||
|  | KeyWriter::KeyWriter(OneWireMaster* _onewire_master) { | ||||||
|  |     onewire_master = _onewire_master; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyWriter::Error KeyWriter::write(iButtonKey* key) { | ||||||
|  |     return write_internal(key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWriter::start() { | ||||||
|  |     onewire_master->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWriter::stop() { | ||||||
|  |     onewire_master->stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyWriter::Error KeyWriter::write_internal(iButtonKey* key) { | ||||||
|  |     Error result = Error::NO_DETECT; | ||||||
|  |     bool same_key = false; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     bool presence = onewire_master->reset(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(presence) { | ||||||
|  |         switch(key->get_key_type()) { | ||||||
|  |         case iButtonKeyType::KeyDallas: | ||||||
|  |             same_key = compare_key_ds1990(key); | ||||||
|  | 
 | ||||||
|  |             if(!same_key) { | ||||||
|  |                 bool write_result = false; | ||||||
|  |                 // currently we can write:
 | ||||||
|  |                 // RW1990, TM08v2, TM08vi-2 by write_1990_1()
 | ||||||
|  |                 // RW2004, RW2004 with EEPROM by write_TM2004();
 | ||||||
|  | 
 | ||||||
|  |                 if(!write_result) { | ||||||
|  |                     write_result = write_1990_1(key); | ||||||
|  |                 } | ||||||
|  |                 if(!write_result) { | ||||||
|  |                     write_result = write_1990_2(key); | ||||||
|  |                 } | ||||||
|  |                 if(!write_result) { | ||||||
|  |                     write_result = write_TM2004(key); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(write_result) { | ||||||
|  |                     result = Error::OK; | ||||||
|  |                 } else { | ||||||
|  |                     result = Error::CANNOT_WRITE; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 result = Error::SAME_KEY; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWriter::compare_key_ds1990(iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|  |         __disable_irq(); | ||||||
|  |         bool presence = onewire_master->reset(); | ||||||
|  | 
 | ||||||
|  |         if(presence) { | ||||||
|  |             onewire_master->write(DS1990::CMD_READ_ROM); | ||||||
|  | 
 | ||||||
|  |             result = true; | ||||||
|  |             for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |                 if(key->get_data()[i] != onewire_master->read()) { | ||||||
|  |                     result = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         __enable_irq(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWriter::write_1990_1(iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|  |         __disable_irq(); | ||||||
|  | 
 | ||||||
|  |         // unlock
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         delay_us(10); | ||||||
|  |         onewire_write_one_bit(0, 5000); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         onewire_master->write(RW1990_1::CMD_WRITE_ROM); | ||||||
|  |         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |             // inverted key for RW1990.1
 | ||||||
|  |             write_byte_ds1990(~key->get_data()[i]); | ||||||
|  |             delay_us(30000); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // lock
 | ||||||
|  |         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         onewire_write_one_bit(1); | ||||||
|  | 
 | ||||||
|  |         __enable_irq(); | ||||||
|  | 
 | ||||||
|  |         if(compare_key_ds1990(key)) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWriter::write_1990_2(iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|  |         __disable_irq(); | ||||||
|  | 
 | ||||||
|  |         // unlock
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         delay_us(10); | ||||||
|  |         onewire_write_one_bit(1, 5000); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         onewire_master->write(RW1990_2::CMD_WRITE_ROM); | ||||||
|  |         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |             write_byte_ds1990(key->get_data()[i]); | ||||||
|  |             delay_us(30000); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // lock
 | ||||||
|  |         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         onewire_write_one_bit(0); | ||||||
|  | 
 | ||||||
|  |         __enable_irq(); | ||||||
|  | 
 | ||||||
|  |         if(compare_key_ds1990(key)) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWriter::write_TM2004(iButtonKey* key) { | ||||||
|  |     uint8_t answer; | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|  |         __disable_irq(); | ||||||
|  | 
 | ||||||
|  |         // write rom, addr is 0x0000
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         onewire_master->write(TM2004::CMD_WRITE_ROM); | ||||||
|  |         onewire_master->write(0x00); | ||||||
|  |         onewire_master->write(0x00); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |             // write key byte
 | ||||||
|  |             onewire_master->write(key->get_data()[i]); | ||||||
|  |             answer = onewire_master->read(); | ||||||
|  |             // TODO: check answer CRC
 | ||||||
|  | 
 | ||||||
|  |             // pulse indicating that data is correct
 | ||||||
|  |             delay_us(600); | ||||||
|  |             onewire_write_one_bit(1, 50000); | ||||||
|  | 
 | ||||||
|  |             // read writed key byte
 | ||||||
|  |             answer = onewire_master->read(); | ||||||
|  | 
 | ||||||
|  |             // check that writed and readed are same
 | ||||||
|  |             if(key->get_data()[i] != answer) { | ||||||
|  |                 result = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!compare_key_ds1990(key)) { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         onewire_master->reset(); | ||||||
|  | 
 | ||||||
|  |         __enable_irq(); | ||||||
|  |     } else { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool KeyWriter::write_TM01(iButtonKey* key) { | ||||||
|  |     /*bool result = true;
 | ||||||
|  | 
 | ||||||
|  |     // TODO test and encoding
 | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     // unlock
 | ||||||
|  |     onewire_master->reset(); | ||||||
|  |     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(1, 10000); | ||||||
|  | 
 | ||||||
|  |     // write key
 | ||||||
|  |     onewire_master->reset(); | ||||||
|  |     onewire_master->write(TM01::CMD_WRITE_ROM); | ||||||
|  | 
 | ||||||
|  |     // TODO: key types
 | ||||||
|  |     //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
 | ||||||
|  |     //} else {
 | ||||||
|  |     for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |         write_byte_ds1990(key->get_data()[i]); | ||||||
|  |         delay_us(10000); | ||||||
|  |     } | ||||||
|  |     //}
 | ||||||
|  | 
 | ||||||
|  |     // lock
 | ||||||
|  |     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(0, 10000); | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  | 
 | ||||||
|  |     if(!compare_key_ds1990(key)) { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     if(key->get_key_type() == iButtonKeyType::KeyMetakom || | ||||||
|  |        key->get_key_type() == iButtonKeyType::KeyCyfral) { | ||||||
|  |         onewire_master->reset(); | ||||||
|  |         if(key->get_key_type() == iButtonKeyType::KeyCyfral) | ||||||
|  |             onewire_master->write(TM01::CMD_SWITCH_TO_CYFRAL); | ||||||
|  |         else | ||||||
|  |             onewire_master->write(TM01::CMD_SWITCH_TO_METAKOM); | ||||||
|  |         onewire_write_one_bit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  | 
 | ||||||
|  |     return result;*/ | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWriter::onewire_write_one_bit(bool value, uint32_t delay) { | ||||||
|  |     onewire_master->write_bit(value); | ||||||
|  |     delay_us(delay); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KeyWriter::write_byte_ds1990(uint8_t data) { | ||||||
|  |     for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { | ||||||
|  |         onewire_master->write_bit(data & 1); | ||||||
|  |         delay_us(5000); | ||||||
|  |         data = data >> 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								applications/ibutton/helpers/key-writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								applications/ibutton/helpers/key-writer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <one_wire_master.h> | ||||||
|  | 
 | ||||||
|  | class KeyWriter { | ||||||
|  | public: | ||||||
|  |     enum class Error : uint8_t { | ||||||
|  |         OK, | ||||||
|  |         SAME_KEY, | ||||||
|  |         NO_DETECT, | ||||||
|  |         CANNOT_WRITE, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     KeyWriter(OneWireMaster* onewire_master); | ||||||
|  |     ~KeyWriter(); | ||||||
|  | 
 | ||||||
|  |     KeyWriter::Error write(iButtonKey* key); | ||||||
|  |     void start(); | ||||||
|  |     void stop(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     OneWireMaster* onewire_master; | ||||||
|  | 
 | ||||||
|  |     KeyWriter::Error write_internal(iButtonKey* key); | ||||||
|  |     bool compare_key_ds1990(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  |     // write strategy
 | ||||||
|  |     bool write_1990_1(iButtonKey* key); | ||||||
|  |     bool write_1990_2(iButtonKey* key); | ||||||
|  |     bool write_TM2004(iButtonKey* key); | ||||||
|  |     bool write_TM01(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  |     void onewire_write_one_bit(bool value, uint32_t delay = 10000); | ||||||
|  |     void write_byte_ds1990(uint8_t data); | ||||||
|  | }; | ||||||
							
								
								
									
										191
									
								
								applications/ibutton/helpers/metakom-decoder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								applications/ibutton/helpers/metakom-decoder.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,191 @@ | |||||||
|  | #include "metakom-decoder.h" | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | bool MetakomDecoder::read(uint8_t* _data, uint8_t data_size) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(ready) { | ||||||
|  |         memcpy(_data, &key_data, 4); | ||||||
|  |         reset_state(); | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MetakomDecoder::process_front(bool polarity, uint32_t time) { | ||||||
|  |     if(max_period == 0) { | ||||||
|  |         max_period = 230 * (SystemCoreClock / 1000000.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(ready) return; | ||||||
|  | 
 | ||||||
|  |     uint32_t high_time = 0; | ||||||
|  |     uint32_t low_time = 0; | ||||||
|  | 
 | ||||||
|  |     switch(state) { | ||||||
|  |     case State::WAIT_PERIOD_SYNC: | ||||||
|  |         if(process_bit(polarity, time, &high_time, &low_time)) { | ||||||
|  |             period_sample_data[period_sample_index] = high_time + low_time; | ||||||
|  |             period_sample_index++; | ||||||
|  | 
 | ||||||
|  |             if(period_sample_index == period_sample_count) { | ||||||
|  |                 for(uint8_t i = 0; i < period_sample_count; i++) { | ||||||
|  |                     period_time += period_sample_data[i]; | ||||||
|  |                 }; | ||||||
|  |                 period_time /= period_sample_count; | ||||||
|  | 
 | ||||||
|  |                 state = State::WAIT_START_BIT; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case State::WAIT_START_BIT: | ||||||
|  |         if(process_bit(polarity, time, &high_time, &low_time)) { | ||||||
|  |             tmp_counter++; | ||||||
|  |             if(high_time > period_time) { | ||||||
|  |                 tmp_counter = 0; | ||||||
|  |                 state = State::WAIT_START_WORD; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(tmp_counter > 40) { | ||||||
|  |                 reset_state(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case State::WAIT_START_WORD: | ||||||
|  |         if(process_bit(polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (period_time / 2)) { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(tmp_counter == 3) { | ||||||
|  |                 if(tmp_data == 0b010) { | ||||||
|  |                     tmp_counter = 0; | ||||||
|  |                     tmp_data = 0; | ||||||
|  |                     state = State::READ_WORD; | ||||||
|  |                 } else { | ||||||
|  |                     reset_state(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case State::READ_WORD: | ||||||
|  |         if(process_bit(polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (period_time / 2)) { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(tmp_counter == 8) { | ||||||
|  |                 if(parity_check(tmp_data)) { | ||||||
|  |                     key_data = (key_data << 8) | tmp_data; | ||||||
|  |                     key_data_index++; | ||||||
|  |                     tmp_data = 0; | ||||||
|  |                     tmp_counter = 0; | ||||||
|  | 
 | ||||||
|  |                     if(key_data_index == 4) { | ||||||
|  |                         // check for stop bit
 | ||||||
|  |                         if(high_time > period_time) { | ||||||
|  |                             state = State::READ_STOP_WORD; | ||||||
|  |                         } else { | ||||||
|  |                             reset_state(); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     reset_state(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case State::READ_STOP_WORD: | ||||||
|  |         if(process_bit(polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (period_time / 2)) { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 tmp_data = (tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(tmp_counter == 3) { | ||||||
|  |                 if(tmp_data == 0b010) { | ||||||
|  |                     ready = true; | ||||||
|  |                 } else { | ||||||
|  |                     reset_state(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MetakomDecoder::MetakomDecoder() { | ||||||
|  |     reset_state(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MetakomDecoder::reset_state() { | ||||||
|  |     ready = false; | ||||||
|  |     period_sample_index = 0; | ||||||
|  |     period_time = 0; | ||||||
|  | 
 | ||||||
|  |     tmp_counter = 0; | ||||||
|  |     tmp_data = 0; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < period_sample_count; i++) { | ||||||
|  |         period_sample_data[i] = 0; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     state = State::WAIT_PERIOD_SYNC; | ||||||
|  |     bit_state = BitState::WAIT_FRONT_LOW; | ||||||
|  | 
 | ||||||
|  |     key_data = 0; | ||||||
|  |     key_data_index = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MetakomDecoder::parity_check(uint8_t data) { | ||||||
|  |     uint8_t ones_count = 0; | ||||||
|  |     bool result; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < 8; i++) { | ||||||
|  |         if((data >> i) & 0b00000001) { | ||||||
|  |             ones_count++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = (ones_count % 2 == 0); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MetakomDecoder::process_bit( | ||||||
|  |     bool polarity, | ||||||
|  |     uint32_t time, | ||||||
|  |     uint32_t* high_time, | ||||||
|  |     uint32_t* low_time) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     switch(bit_state) { | ||||||
|  |     case BitState::WAIT_FRONT_LOW: | ||||||
|  |         if(polarity == false) { | ||||||
|  |             *low_time = low_time_storage; | ||||||
|  |             *high_time = time; | ||||||
|  |             result = true; | ||||||
|  |             bit_state = BitState::WAIT_FRONT_HIGH; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case BitState::WAIT_FRONT_HIGH: | ||||||
|  |         if(polarity == true) { | ||||||
|  |             low_time_storage = time; | ||||||
|  |             bit_state = BitState::WAIT_FRONT_LOW; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								applications/ibutton/helpers/metakom-decoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/ibutton/helpers/metakom-decoder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | class MetakomDecoder { | ||||||
|  | public: | ||||||
|  |     bool read(uint8_t* data, uint8_t data_size); | ||||||
|  |     void process_front(bool polarity, uint32_t time); | ||||||
|  | 
 | ||||||
|  |     MetakomDecoder(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     enum class BitState : uint8_t { | ||||||
|  |         WAIT_FRONT_HIGH, | ||||||
|  |         WAIT_FRONT_LOW, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     BitState bit_state; | ||||||
|  | 
 | ||||||
|  |     enum class State : uint8_t { | ||||||
|  |         WAIT_PERIOD_SYNC, | ||||||
|  |         WAIT_START_BIT, | ||||||
|  |         WAIT_START_WORD, | ||||||
|  |         READ_WORD, | ||||||
|  |         READ_STOP_WORD, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     State state; | ||||||
|  | 
 | ||||||
|  |     // high + low period time
 | ||||||
|  |     uint32_t period_time; | ||||||
|  |     uint32_t low_time_storage; | ||||||
|  | 
 | ||||||
|  |     static const uint8_t period_sample_count = 10; | ||||||
|  |     uint8_t period_sample_index; | ||||||
|  |     uint32_t period_sample_data[period_sample_count]; | ||||||
|  | 
 | ||||||
|  |     // ready flag, key is readed and valid
 | ||||||
|  |     std::atomic<bool> ready; | ||||||
|  | 
 | ||||||
|  |     // max period, 230us x clock per us
 | ||||||
|  |     uint32_t max_period; | ||||||
|  | 
 | ||||||
|  |     uint8_t tmp_data; | ||||||
|  |     uint8_t tmp_counter; | ||||||
|  | 
 | ||||||
|  |     uint32_t key_data; | ||||||
|  |     uint8_t key_data_index; | ||||||
|  | 
 | ||||||
|  |     void reset_state(); | ||||||
|  |     bool parity_check(uint8_t data); | ||||||
|  | 
 | ||||||
|  |     bool process_bit(bool polarity, uint32_t time, uint32_t* high_time, uint32_t* low_time); | ||||||
|  | }; | ||||||
							
								
								
									
										87
									
								
								applications/ibutton/helpers/pulse-sequencer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								applications/ibutton/helpers/pulse-sequencer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | #include "pulse-sequencer.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <callback-connector.h> | ||||||
|  | #include <api-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::set_periods( | ||||||
|  |     uint32_t* _periods, | ||||||
|  |     uint16_t _periods_count, | ||||||
|  |     bool _pin_start_state) { | ||||||
|  |     periods = _periods; | ||||||
|  |     periods_count = _periods_count; | ||||||
|  |     pin_start_state = _pin_start_state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::start() { | ||||||
|  |     callback_pointer = cbc::obtain_connector(this, &PulseSequencer::timer_elapsed_callback); | ||||||
|  |     api_interrupt_add(callback_pointer, InterruptTypeTimerUpdate, this); | ||||||
|  | 
 | ||||||
|  |     init_timer(periods[period_index]); | ||||||
|  |     pin_state = pin_start_state; | ||||||
|  |     gpio_write(&ibutton_gpio, pin_state); | ||||||
|  |     pin_state = !pin_state; | ||||||
|  |     period_index = 1; | ||||||
|  | 
 | ||||||
|  |     HAL_TIM_Base_Start_IT(&htim1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::stop() { | ||||||
|  |     HAL_TIM_Base_Stop_IT(&htim1); | ||||||
|  | 
 | ||||||
|  |     api_interrupt_remove(callback_pointer, InterruptTypeTimerUpdate); | ||||||
|  |     deinit_timer(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PulseSequencer::~PulseSequencer() { | ||||||
|  |     stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::init_timer(uint32_t period) { | ||||||
|  |     TIM_ClockConfigTypeDef sClockSourceConfig = {0}; | ||||||
|  | 
 | ||||||
|  |     htim1.Instance = TIM1; | ||||||
|  |     htim1.Init.Prescaler = 0; | ||||||
|  |     htim1.Init.CounterMode = TIM_COUNTERMODE_UP; | ||||||
|  |     htim1.Init.Period = period; | ||||||
|  |     htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; | ||||||
|  |     htim1.Init.RepetitionCounter = 0; | ||||||
|  |     htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; | ||||||
|  |     if(HAL_TIM_Base_Init(&htim1) != HAL_OK) { | ||||||
|  |         Error_Handler(); | ||||||
|  |     } | ||||||
|  |     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; | ||||||
|  |     if(HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { | ||||||
|  |         Error_Handler(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0); | ||||||
|  |     HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); | ||||||
|  | 
 | ||||||
|  |     gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::deinit_timer() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PulseSequencer::timer_elapsed_callback(void* hw, void* context) { | ||||||
|  |     PulseSequencer* _this = static_cast<PulseSequencer*>(context); | ||||||
|  |     TIM_HandleTypeDef* htim = static_cast<TIM_HandleTypeDef*>(hw); | ||||||
|  | 
 | ||||||
|  |     if(htim->Instance == TIM1) { | ||||||
|  |         htim->Instance->ARR = _this->periods[_this->period_index]; | ||||||
|  | 
 | ||||||
|  |         if(_this->period_index == 0) { | ||||||
|  |             _this->pin_state = _this->pin_start_state; | ||||||
|  |         } else { | ||||||
|  |             _this->pin_state = !_this->pin_state; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         gpio_write(&ibutton_gpio, _this->pin_state); | ||||||
|  | 
 | ||||||
|  |         _this->period_index++; | ||||||
|  | 
 | ||||||
|  |         if(_this->period_index == _this->periods_count) { | ||||||
|  |             _this->period_index = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								applications/ibutton/helpers/pulse-sequencer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								applications/ibutton/helpers/pulse-sequencer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | class PulseSequencer { | ||||||
|  | public: | ||||||
|  |     void set_periods(uint32_t* periods, uint16_t periods_count, bool pin_start_state); | ||||||
|  |     void start(); | ||||||
|  |     void stop(); | ||||||
|  | 
 | ||||||
|  |     ~PulseSequencer(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     uint16_t period_index; | ||||||
|  |     uint16_t periods_count; | ||||||
|  |     uint32_t* periods; | ||||||
|  |     bool pin_start_state; | ||||||
|  |     bool pin_state; | ||||||
|  | 
 | ||||||
|  |     void init_timer(uint32_t period); | ||||||
|  |     void deinit_timer(); | ||||||
|  | 
 | ||||||
|  |     void reset_period_index(PulseSequencer* _this); | ||||||
|  | 
 | ||||||
|  |     void (*callback_pointer)(void*, void*); | ||||||
|  |     void timer_elapsed_callback(void* hcomp, void* comp_ctx); | ||||||
|  | }; | ||||||
							
								
								
									
										238
									
								
								applications/ibutton/ibutton-app.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								applications/ibutton/ibutton-app.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,238 @@ | |||||||
|  | #include "ibutton-app.h" | ||||||
|  | #include <stdarg.h> | ||||||
|  | 
 | ||||||
|  | void iButtonApp::run(void) { | ||||||
|  |     iButtonEvent event; | ||||||
|  |     bool consumed; | ||||||
|  |     bool exit = false; | ||||||
|  | 
 | ||||||
|  |     scenes[current_scene]->on_enter(this); | ||||||
|  | 
 | ||||||
|  |     while(!exit) { | ||||||
|  |         view.receive_event(&event); | ||||||
|  | 
 | ||||||
|  |         consumed = scenes[current_scene]->on_event(this, &event); | ||||||
|  | 
 | ||||||
|  |         if(!consumed) { | ||||||
|  |             if(event.type == iButtonEvent::Type::EventTypeBack) { | ||||||
|  |                 exit = switch_to_previous_scene(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     scenes[current_scene]->on_exit(this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonApp::iButtonApp() { | ||||||
|  |     notify_init(); | ||||||
|  |     api_hal_power_insomnia_enter(); | ||||||
|  | 
 | ||||||
|  |     key_worker = new KeyWorker(&ibutton_gpio); | ||||||
|  | 
 | ||||||
|  |     // we need random
 | ||||||
|  |     srand(DWT->CYCCNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonApp::~iButtonApp() { | ||||||
|  |     api_hal_power_insomnia_exit(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonAppViewManager* iButtonApp::get_view_manager() { | ||||||
|  |     return &view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::switch_to_next_scene(Scene next_scene) { | ||||||
|  |     previous_scenes_list.push_front(current_scene); | ||||||
|  | 
 | ||||||
|  |     if(next_scene != Scene::SceneExit) { | ||||||
|  |         scenes[current_scene]->on_exit(this); | ||||||
|  |         current_scene = next_scene; | ||||||
|  |         scenes[current_scene]->on_enter(this); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) { | ||||||
|  |     Scene previous_scene = Scene::SceneStart; | ||||||
|  |     bool scene_found = false; | ||||||
|  | 
 | ||||||
|  |     while(!scene_found) { | ||||||
|  |         previous_scene = get_previous_scene(); | ||||||
|  |         for(Scene element : scenes_list) { | ||||||
|  |             if(previous_scene == element || previous_scene == Scene::SceneStart) { | ||||||
|  |                 scene_found = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     scenes[current_scene]->on_exit(this); | ||||||
|  |     current_scene = previous_scene; | ||||||
|  |     scenes[current_scene]->on_enter(this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonApp::switch_to_previous_scene(uint8_t count) { | ||||||
|  |     Scene previous_scene = Scene::SceneStart; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < count; i++) { | ||||||
|  |         previous_scene = get_previous_scene(); | ||||||
|  |         if(previous_scene == Scene::SceneExit) break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(previous_scene == Scene::SceneExit) { | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         scenes[current_scene]->on_exit(this); | ||||||
|  |         current_scene = previous_scene; | ||||||
|  |         scenes[current_scene]->on_enter(this); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonApp::Scene iButtonApp::get_previous_scene() { | ||||||
|  |     Scene scene = previous_scenes_list.front(); | ||||||
|  |     previous_scenes_list.pop_front(); | ||||||
|  |     return scene; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const GpioPin* iButtonApp::get_ibutton_pin() { | ||||||
|  |     // TODO open record
 | ||||||
|  |     return &ibutton_gpio; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyWorker* iButtonApp::get_key_worker() { | ||||||
|  |     return key_worker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKey* iButtonApp::get_key() { | ||||||
|  |     return &key; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_init() { | ||||||
|  |     // TODO open record
 | ||||||
|  |     const GpioPin* vibro_record = &vibro_gpio; | ||||||
|  |     gpio_init(vibro_record, GpioModeOutputPushPull); | ||||||
|  |     gpio_write(vibro_record, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_green_blink() { | ||||||
|  |     notify_green_on(); | ||||||
|  |     delay(10); | ||||||
|  |     notify_green_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_yellow_blink() { | ||||||
|  |     notify_red_on(); | ||||||
|  |     notify_green_on(); | ||||||
|  |     delay(10); | ||||||
|  |     notify_green_off(); | ||||||
|  |     notify_red_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_red_blink() { | ||||||
|  |     notify_red_on(); | ||||||
|  |     delay(10); | ||||||
|  |     notify_red_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_green_on() { | ||||||
|  |     api_hal_light_set(LightGreen, 0xFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_green_off() { | ||||||
|  |     api_hal_light_set(LightGreen, 0x00); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_red_on() { | ||||||
|  |     api_hal_light_set(LightRed, 0xFF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_red_off() { | ||||||
|  |     api_hal_light_set(LightRed, 0x00); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_error() { | ||||||
|  |     notify_vibro_on(); | ||||||
|  |     delay(50); | ||||||
|  |     notify_vibro_off(); | ||||||
|  |     delay(100); | ||||||
|  |     notify_vibro_on(); | ||||||
|  |     delay(50); | ||||||
|  |     notify_vibro_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_success() { | ||||||
|  |     notify_vibro_on(); | ||||||
|  |     hal_pwm_set(0.5, 1760, &SPEAKER_TIM, SPEAKER_CH); | ||||||
|  |     delay(50); | ||||||
|  |     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||||
|  |     notify_vibro_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_vibro_on() { | ||||||
|  |     gpio_write(&vibro_gpio, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_vibro_off() { | ||||||
|  |     gpio_write(&vibro_gpio, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::set_text_store(const char* text...) { | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, text); | ||||||
|  | 
 | ||||||
|  |     vsnprintf(text_store, text_store_size, text, args); | ||||||
|  | 
 | ||||||
|  |     va_end(args); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char* iButtonApp::get_text_store() { | ||||||
|  |     return text_store; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t iButtonApp::get_text_store_size() { | ||||||
|  |     return text_store_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyStore* iButtonApp::get_key_store() { | ||||||
|  |     return &store; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t iButtonApp::get_stored_key_index() { | ||||||
|  |     return key_index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::set_stored_key_index(uint8_t _index) { | ||||||
|  |     key_index = _index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) { | ||||||
|  |     const uint8_t prefix_size = 9; | ||||||
|  |     const char* prefix[prefix_size] = { | ||||||
|  |         "ancient", | ||||||
|  |         "hollow", | ||||||
|  |         "strange", | ||||||
|  |         "disappeared", | ||||||
|  |         "unknown", | ||||||
|  |         "unthinkable", | ||||||
|  |         "unnamable", | ||||||
|  |         "nameless", | ||||||
|  |         "my", | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const uint8_t suffix_size = 8; | ||||||
|  |     const char* suffix[suffix_size] = { | ||||||
|  |         "door", | ||||||
|  |         "entrance", | ||||||
|  |         "doorway", | ||||||
|  |         "entry", | ||||||
|  |         "portal", | ||||||
|  |         "entree", | ||||||
|  |         "opening", | ||||||
|  |         "crack", | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     sniprintf( | ||||||
|  |         name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]); | ||||||
|  |     // to upper
 | ||||||
|  |     name[0] = name[0] - 0x20; | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								applications/ibutton/ibutton-app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								applications/ibutton/ibutton-app.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <map> | ||||||
|  | #include <list> | ||||||
|  | 
 | ||||||
|  | #include "ibutton-view-manager.h" | ||||||
|  | #include "scene/ibutton-scene-generic.h" | ||||||
|  | #include "scene/ibutton-scene-start.h" | ||||||
|  | #include "scene/ibutton-scene-read.h" | ||||||
|  | #include "scene/ibutton-scene-read-crc-error.h" | ||||||
|  | #include "scene/ibutton-scene-read-not-key-error.h" | ||||||
|  | #include "scene/ibutton-scene-read-success.h" | ||||||
|  | #include "scene/ibutton-scene-readed-key-menu.h" | ||||||
|  | #include "scene/ibutton-scene-write.h" | ||||||
|  | #include "scene/ibutton-scene-write-success.h" | ||||||
|  | #include "scene/ibutton-scene-saved.h" | ||||||
|  | #include "scene/ibutton-scene-saved-key-menu.h" | ||||||
|  | #include "scene/ibutton-scene-delete-confirm.h" | ||||||
|  | #include "scene/ibutton-scene-delete-success.h" | ||||||
|  | #include "scene/ibutton-scene-emulate.h" | ||||||
|  | #include "scene/ibutton-scene-save-name.h" | ||||||
|  | #include "scene/ibutton-scene-save-success.h" | ||||||
|  | #include "scene/ibutton-scene-info.h" | ||||||
|  | #include "scene/ibutton-scene-add-type.h" | ||||||
|  | #include "scene/ibutton-scene-add-value.h" | ||||||
|  | 
 | ||||||
|  | #include "helpers/key-store.h" | ||||||
|  | #include "helpers/key-worker.h" | ||||||
|  | 
 | ||||||
|  | #include "one_wire_master.h" | ||||||
|  | #include "maxim_crc.h" | ||||||
|  | #include "ibutton-key.h" | ||||||
|  | 
 | ||||||
|  | class iButtonApp { | ||||||
|  | public: | ||||||
|  |     void run(void); | ||||||
|  | 
 | ||||||
|  |     iButtonApp(); | ||||||
|  |     ~iButtonApp(); | ||||||
|  | 
 | ||||||
|  |     enum class Scene : uint8_t { | ||||||
|  |         SceneExit, | ||||||
|  |         SceneStart, | ||||||
|  |         SceneRead, | ||||||
|  |         SceneReadNotKeyError, | ||||||
|  |         SceneReadCRCError, | ||||||
|  |         SceneReadSuccess, | ||||||
|  |         SceneReadedKeyMenu, | ||||||
|  |         SceneWrite, | ||||||
|  |         SceneWriteSuccess, | ||||||
|  |         SceneEmulate, | ||||||
|  |         SceneSavedList, | ||||||
|  |         SceneSavedKeyMenu, | ||||||
|  |         SceneDeleteConfirm, | ||||||
|  |         SceneDeleteSuccess, | ||||||
|  |         SceneSaveName, | ||||||
|  |         SceneSaveSuccess, | ||||||
|  |         SceneInfo, | ||||||
|  |         SceneAddType, | ||||||
|  |         SceneAddValue, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     iButtonAppViewManager* get_view_manager(); | ||||||
|  |     void switch_to_next_scene(Scene index); | ||||||
|  |     void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list); | ||||||
|  |     bool switch_to_previous_scene(uint8_t count = 1); | ||||||
|  |     Scene get_previous_scene(); | ||||||
|  | 
 | ||||||
|  |     const GpioPin* get_ibutton_pin(); | ||||||
|  |     KeyWorker* get_key_worker(); | ||||||
|  |     iButtonKey* get_key(); | ||||||
|  | 
 | ||||||
|  |     void notify_green_blink(); | ||||||
|  |     void notify_yellow_blink(); | ||||||
|  |     void notify_red_blink(); | ||||||
|  | 
 | ||||||
|  |     void notify_green_on(); | ||||||
|  |     void notify_green_off(); | ||||||
|  |     void notify_red_on(); | ||||||
|  |     void notify_red_off(); | ||||||
|  | 
 | ||||||
|  |     void notify_error(); | ||||||
|  |     void notify_success(); | ||||||
|  | 
 | ||||||
|  |     void notify_vibro_on(); | ||||||
|  |     void notify_vibro_off(); | ||||||
|  | 
 | ||||||
|  |     void set_text_store(const char* text...); | ||||||
|  |     char* get_text_store(); | ||||||
|  |     uint8_t get_text_store_size(); | ||||||
|  | 
 | ||||||
|  |     KeyStore* get_key_store(); | ||||||
|  |     uint8_t get_stored_key_index(); | ||||||
|  |     void set_stored_key_index(uint8_t index); | ||||||
|  | 
 | ||||||
|  |     void generate_random_name(char* name, uint8_t max_name_size); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::list<Scene> previous_scenes_list = {Scene::SceneExit}; | ||||||
|  |     Scene current_scene = Scene::SceneStart; | ||||||
|  |     iButtonAppViewManager view; | ||||||
|  | 
 | ||||||
|  |     std::map<Scene, iButtonScene*> scenes = { | ||||||
|  |         {Scene::SceneStart, new iButtonSceneStart()}, | ||||||
|  |         {Scene::SceneRead, new iButtonSceneRead()}, | ||||||
|  |         {Scene::SceneReadCRCError, new iButtonSceneReadCRCError()}, | ||||||
|  |         {Scene::SceneReadNotKeyError, new iButtonSceneReadNotKeyError()}, | ||||||
|  |         {Scene::SceneReadSuccess, new iButtonSceneReadSuccess()}, | ||||||
|  |         {Scene::SceneReadedKeyMenu, new iButtonSceneReadedKeyMenu()}, | ||||||
|  |         {Scene::SceneWrite, new iButtonSceneWrite()}, | ||||||
|  |         {Scene::SceneWriteSuccess, new iButtonSceneWriteSuccess()}, | ||||||
|  |         {Scene::SceneEmulate, new iButtonSceneEmulate()}, | ||||||
|  |         {Scene::SceneSavedList, new iButtonSceneSavedList()}, | ||||||
|  |         {Scene::SceneSavedKeyMenu, new iButtonSceneSavedKeyMenu()}, | ||||||
|  |         {Scene::SceneDeleteConfirm, new iButtonSceneDeleteConfirm()}, | ||||||
|  |         {Scene::SceneDeleteSuccess, new iButtonSceneDeleteSuccess()}, | ||||||
|  |         {Scene::SceneSaveName, new iButtonSceneSaveName()}, | ||||||
|  |         {Scene::SceneSaveSuccess, new iButtonSceneSaveSuccess()}, | ||||||
|  |         {Scene::SceneInfo, new iButtonSceneInfo()}, | ||||||
|  |         {Scene::SceneAddType, new iButtonSceneAddType()}, | ||||||
|  |         {Scene::SceneAddValue, new iButtonSceneAddValue()}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     KeyWorker* key_worker; | ||||||
|  | 
 | ||||||
|  |     iButtonKey key; | ||||||
|  |     uint8_t key_index = 0; | ||||||
|  | 
 | ||||||
|  |     static const uint8_t text_store_size = 128; | ||||||
|  |     char text_store[text_store_size + 1]; | ||||||
|  | 
 | ||||||
|  |     KeyStore store; | ||||||
|  | 
 | ||||||
|  |     void notify_init(); | ||||||
|  | }; | ||||||
							
								
								
									
										27
									
								
								applications/ibutton/ibutton-event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/ibutton/ibutton-event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonApp; | ||||||
|  | 
 | ||||||
|  | class iButtonEvent { | ||||||
|  | public: | ||||||
|  |     // events enum
 | ||||||
|  |     enum class Type : uint8_t { | ||||||
|  |         EventTypeTick, | ||||||
|  |         EventTypeBack, | ||||||
|  |         EventTypeMenuSelected, | ||||||
|  |         EventTypeDialogResult, | ||||||
|  |         EventTypeTextEditResult, | ||||||
|  |         EventTypeByteEditResult, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // payload
 | ||||||
|  |     union { | ||||||
|  |         uint32_t menu_index; | ||||||
|  |         DialogExResult dialog_result; | ||||||
|  |     } payload; | ||||||
|  | 
 | ||||||
|  |     // event type
 | ||||||
|  |     Type type; | ||||||
|  | }; | ||||||
							
								
								
									
										78
									
								
								applications/ibutton/ibutton-key.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								applications/ibutton/ibutton-key.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | #include "ibutton-key.h" | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | uint8_t iButtonKey::get_size() { | ||||||
|  |     return IBUTTON_KEY_SIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonKey::set_data(uint8_t* _data, uint8_t _data_count) { | ||||||
|  |     furi_check(_data_count > 0); | ||||||
|  |     furi_check(_data_count <= get_size()); | ||||||
|  | 
 | ||||||
|  |     memset(data, 0, get_size()); | ||||||
|  |     memcpy(data, _data, _data_count); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t* iButtonKey::get_data() { | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t iButtonKey::get_type_data_size() { | ||||||
|  |     uint8_t size = 0; | ||||||
|  | 
 | ||||||
|  |     switch(type) { | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         size = 2; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         size = 4; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         size = 8; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonKey::set_name(const char* _name) { | ||||||
|  |     name = _name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* iButtonKey::get_name() { | ||||||
|  |     return name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonKey::set_type(iButtonKeyType _key_type) { | ||||||
|  |     type = _key_type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKeyType iButtonKey::get_key_type() { | ||||||
|  |     return type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKey::iButtonKey( | ||||||
|  |     iButtonKeyType _type, | ||||||
|  |     const char* _name, | ||||||
|  |     uint8_t d0, | ||||||
|  |     uint8_t d1, | ||||||
|  |     uint8_t d2, | ||||||
|  |     uint8_t d3, | ||||||
|  |     uint8_t d4, | ||||||
|  |     uint8_t d5, | ||||||
|  |     uint8_t d6, | ||||||
|  |     uint8_t d7) { | ||||||
|  |     type = _type; | ||||||
|  |     name = _name; | ||||||
|  |     data[0] = d0; | ||||||
|  |     data[1] = d1; | ||||||
|  |     data[2] = d2; | ||||||
|  |     data[3] = d3; | ||||||
|  |     data[4] = d4; | ||||||
|  |     data[5] = d5; | ||||||
|  |     data[6] = d6; | ||||||
|  |     data[7] = d7; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKey::iButtonKey() { | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								applications/ibutton/ibutton-key.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								applications/ibutton/ibutton-key.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "helpers/key-info.h" | ||||||
|  | 
 | ||||||
|  | class iButtonKey { | ||||||
|  | public: | ||||||
|  |     uint8_t get_size(); | ||||||
|  | 
 | ||||||
|  |     void set_data(uint8_t* data, uint8_t data_count); | ||||||
|  |     uint8_t* get_data(); | ||||||
|  |     uint8_t get_type_data_size(); | ||||||
|  | 
 | ||||||
|  |     void set_name(const char* name); | ||||||
|  |     const char* get_name(); | ||||||
|  | 
 | ||||||
|  |     void set_type(iButtonKeyType key_type); | ||||||
|  |     iButtonKeyType get_key_type(); | ||||||
|  | 
 | ||||||
|  |     // temporary constructor for KeyStore mockup
 | ||||||
|  |     iButtonKey( | ||||||
|  |         iButtonKeyType type, | ||||||
|  |         const char* name, | ||||||
|  |         uint8_t d0, | ||||||
|  |         uint8_t d1, | ||||||
|  |         uint8_t d2, | ||||||
|  |         uint8_t d3, | ||||||
|  |         uint8_t d4, | ||||||
|  |         uint8_t d5, | ||||||
|  |         uint8_t d6, | ||||||
|  |         uint8_t d7); | ||||||
|  | 
 | ||||||
|  |     iButtonKey(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     uint8_t data[IBUTTON_KEY_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||||
|  |     const char* name = {0}; | ||||||
|  | 
 | ||||||
|  |     iButtonKeyType type = iButtonKeyType::KeyDallas; | ||||||
|  | }; | ||||||
							
								
								
									
										126
									
								
								applications/ibutton/ibutton-view-manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								applications/ibutton/ibutton-view-manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | #include "ibutton-view-manager.h" | ||||||
|  | #include "ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | iButtonAppViewManager::iButtonAppViewManager() { | ||||||
|  |     event_queue = osMessageQueueNew(10, sizeof(iButtonEvent), NULL); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonAppViewManager::previous_view_callback); | ||||||
|  | 
 | ||||||
|  |     dialog_ex = dialog_ex_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewDialogEx), | ||||||
|  |         dialog_ex_get_view(dialog_ex)); | ||||||
|  | 
 | ||||||
|  |     submenu = submenu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewSubmenu), | ||||||
|  |         submenu_get_view(submenu)); | ||||||
|  | 
 | ||||||
|  |     text_input = text_input_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewTextInput), | ||||||
|  |         text_input_get_view(text_input)); | ||||||
|  | 
 | ||||||
|  |     byte_input = byte_input_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewByteInput), | ||||||
|  |         byte_input_get_view(byte_input)); | ||||||
|  | 
 | ||||||
|  |     popup = popup_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewPopup), | ||||||
|  |         popup_get_view(popup)); | ||||||
|  | 
 | ||||||
|  |     gui = static_cast<Gui*>(furi_record_open("gui")); | ||||||
|  |     view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     //TODO think about that method, seems unsafe and over-engineered
 | ||||||
|  |     view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback); | ||||||
|  |     view_set_previous_callback(submenu_get_view(submenu), callback); | ||||||
|  |     view_set_previous_callback(text_input_get_view(text_input), callback); | ||||||
|  |     view_set_previous_callback(byte_input_get_view(byte_input), callback); | ||||||
|  |     view_set_previous_callback(popup_get_view(popup), callback); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonAppViewManager::~iButtonAppViewManager() { | ||||||
|  |     // remove views
 | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewDialogEx)); | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewSubmenu)); | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewTextInput)); | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewPopup)); | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, | ||||||
|  |         static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewByteInput)); | ||||||
|  | 
 | ||||||
|  |     // free view modules
 | ||||||
|  |     popup_free(popup); | ||||||
|  |     text_input_free(text_input); | ||||||
|  |     byte_input_free(byte_input); | ||||||
|  |     submenu_free(submenu); | ||||||
|  |     dialog_ex_free(dialog_ex); | ||||||
|  | 
 | ||||||
|  |     // free dispatcher
 | ||||||
|  |     view_dispatcher_free(view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     // free event queue
 | ||||||
|  |     osMessageQueueDelete(event_queue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonAppViewManager::switch_to(Type type) { | ||||||
|  |     view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Submenu* iButtonAppViewManager::get_submenu() { | ||||||
|  |     return submenu; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Popup* iButtonAppViewManager::get_popup() { | ||||||
|  |     return popup; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DialogEx* iButtonAppViewManager::get_dialog_ex() { | ||||||
|  |     return dialog_ex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TextInput* iButtonAppViewManager::get_text_input() { | ||||||
|  |     return text_input; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ByteInput* iButtonAppViewManager::get_byte_input() { | ||||||
|  |     return byte_input; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonAppViewManager::receive_event(iButtonEvent* event) { | ||||||
|  |     if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { | ||||||
|  |         event->type = iButtonEvent::Type::EventTypeTick; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonAppViewManager::send_event(iButtonEvent* event) { | ||||||
|  |     osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0); | ||||||
|  |     furi_check(result == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t iButtonAppViewManager::previous_view_callback(void* context) { | ||||||
|  |     if(event_queue != NULL) { | ||||||
|  |         iButtonEvent event; | ||||||
|  |         event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |         send_event(&event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return VIEW_IGNORE; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								applications/ibutton/ibutton-view-manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								applications/ibutton/ibutton-view-manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <furi.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/byte_input.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include "ibutton-event.h" | ||||||
|  | 
 | ||||||
|  | class iButtonAppViewManager { | ||||||
|  | public: | ||||||
|  |     enum class Type : uint8_t { | ||||||
|  |         iButtonAppViewTextInput, | ||||||
|  |         iButtonAppViewByteInput, | ||||||
|  |         iButtonAppViewSubmenu, | ||||||
|  |         iButtonAppViewDialogEx, | ||||||
|  |         iButtonAppViewPopup, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     osMessageQueueId_t event_queue; | ||||||
|  | 
 | ||||||
|  |     iButtonAppViewManager(); | ||||||
|  |     ~iButtonAppViewManager(); | ||||||
|  | 
 | ||||||
|  |     void switch_to(Type type); | ||||||
|  | 
 | ||||||
|  |     Submenu* get_submenu(); | ||||||
|  |     Popup* get_popup(); | ||||||
|  |     DialogEx* get_dialog_ex(); | ||||||
|  |     TextInput* get_text_input(); | ||||||
|  |     ByteInput* get_byte_input(); | ||||||
|  | 
 | ||||||
|  |     void receive_event(iButtonEvent* event); | ||||||
|  |     void send_event(iButtonEvent* event); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     DialogEx* dialog_ex; | ||||||
|  |     Submenu* submenu; | ||||||
|  |     TextInput* text_input; | ||||||
|  |     ByteInput* byte_input; | ||||||
|  |     Popup* popup; | ||||||
|  |     Gui* gui; | ||||||
|  | 
 | ||||||
|  |     uint32_t previous_view_callback(void* context); | ||||||
|  | }; | ||||||
| @ -1,173 +1,10 @@ | |||||||
| #include "ibutton.h" | #include "ibutton-app.h" | ||||||
| #include "ibutton_mode_dallas_read.h" |  | ||||||
| #include "ibutton_mode_dallas_emulate.h" |  | ||||||
| #include "ibutton_mode_dallas_write.h" |  | ||||||
| #include "ibutton_mode_cyfral_read.h" |  | ||||||
| #include "ibutton_mode_cyfral_emulate.h" |  | ||||||
| 
 |  | ||||||
| // start app
 |  | ||||||
| void AppiButton::run() { |  | ||||||
|     mode[0] = new AppiButtonModeDallasRead(this); |  | ||||||
|     mode[1] = new AppiButtonModeDallasEmulate(this); |  | ||||||
|     mode[2] = new AppiButtonModeDallasWrite(this); |  | ||||||
|     mode[3] = new AppiButtonModeCyfralRead(this); |  | ||||||
|     mode[4] = new AppiButtonModeCyfralEmulate(this); |  | ||||||
| 
 |  | ||||||
|     switch_to_mode(0); |  | ||||||
| 
 |  | ||||||
|     api_hal_power_insomnia_enter(); |  | ||||||
|     app_ready(); |  | ||||||
| 
 |  | ||||||
|     AppiButtonEvent event; |  | ||||||
|     while(1) { |  | ||||||
|         if(get_event(&event, 1024 / 8)) { |  | ||||||
|             if(event.type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|                 // press events
 |  | ||||||
|                 if(event.value.input.type == InputTypeShort && |  | ||||||
|                    event.value.input.key == InputKeyBack) { |  | ||||||
|                     view_port_enabled_set(view_port, false); |  | ||||||
|                     gui_remove_view_port(gui, view_port); |  | ||||||
|                     api_hal_power_insomnia_exit(); |  | ||||||
| 
 |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(event.value.input.type == InputTypeShort && |  | ||||||
|                    event.value.input.key == InputKeyLeft) { |  | ||||||
|                     decrease_mode(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(event.value.input.type == InputTypeShort && |  | ||||||
|                    event.value.input.key == InputKeyRight) { |  | ||||||
|                     increase_mode(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             event.type = AppiButtonEvent::EventTypeTick; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         acquire_state(); |  | ||||||
|         mode[state.mode_index]->event(&event, &state); |  | ||||||
|         release_state(); |  | ||||||
| 
 |  | ||||||
|         view_port_update(view_port); |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // render app
 |  | ||||||
| void AppiButton::render(Canvas* canvas) { |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     canvas_draw_str(canvas, 2, 12, "iButton"); |  | ||||||
| 
 |  | ||||||
|     mode[state.mode_index]->render(canvas, &state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::render_dallas_list(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     const uint8_t buffer_size = 50; |  | ||||||
|     char buf[buffer_size]; |  | ||||||
|     for(uint8_t i = 0; i < state->dallas_address_count; i++) { |  | ||||||
|         snprintf( |  | ||||||
|             buf, |  | ||||||
|             buffer_size, |  | ||||||
|             "%s[%u] %x:%x:%x:%x:%x:%x:%x:%x", |  | ||||||
|             (i == state->dallas_address_index) ? "> " : "", |  | ||||||
|             i + 1, |  | ||||||
|             state->dallas_address[i][0], |  | ||||||
|             state->dallas_address[i][1], |  | ||||||
|             state->dallas_address[i][2], |  | ||||||
|             state->dallas_address[i][3], |  | ||||||
|             state->dallas_address[i][4], |  | ||||||
|             state->dallas_address[i][5], |  | ||||||
|             state->dallas_address[i][6], |  | ||||||
|             state->dallas_address[i][7]); |  | ||||||
|         canvas_draw_str(canvas, 2, 37 + i * 12, buf); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::render_cyfral_list(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     const uint8_t buffer_size = 50; |  | ||||||
|     char buf[buffer_size]; |  | ||||||
|     for(uint8_t i = 0; i < state->cyfral_address_count; i++) { |  | ||||||
|         snprintf( |  | ||||||
|             buf, |  | ||||||
|             buffer_size, |  | ||||||
|             "%s[%u] %x:%x:%x:%x", |  | ||||||
|             (i == state->cyfral_address_index) ? "> " : "", |  | ||||||
|             i + 1, |  | ||||||
|             state->cyfral_address[i][0], |  | ||||||
|             state->cyfral_address[i][1], |  | ||||||
|             state->cyfral_address[i][2], |  | ||||||
|             state->cyfral_address[i][3]); |  | ||||||
|         canvas_draw_str(canvas, 2, 37 + i * 12, buf); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::blink_red() { |  | ||||||
|     api_hal_light_set(LightRed, 0xFF); |  | ||||||
|     delay(10); |  | ||||||
|     api_hal_light_set(LightRed, 0x00); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::blink_green() { |  | ||||||
|     api_hal_light_set(LightGreen, 0xFF); |  | ||||||
|     delay(10); |  | ||||||
|     api_hal_light_set(LightGreen, 0x00); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::increase_mode() { |  | ||||||
|     acquire_state(); |  | ||||||
|     if(state.mode_index < (modes_count - 1)) { |  | ||||||
|         mode[state.mode_index]->release(); |  | ||||||
|         state.mode_index++; |  | ||||||
|         mode[state.mode_index]->acquire(); |  | ||||||
|     } |  | ||||||
|     release_state(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::decrease_mode() { |  | ||||||
|     acquire_state(); |  | ||||||
|     if(state.mode_index > 0) { |  | ||||||
|         mode[state.mode_index]->release(); |  | ||||||
|         state.mode_index--; |  | ||||||
|         mode[state.mode_index]->acquire(); |  | ||||||
|     } |  | ||||||
|     release_state(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::increase_dallas_address() { |  | ||||||
|     if(state.dallas_address_index < (state.dallas_address_count - 1)) { |  | ||||||
|         state.dallas_address_index++; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::decrease_dallas_address() { |  | ||||||
|     if(state.dallas_address_index > 0) { |  | ||||||
|         state.dallas_address_index--; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::increase_cyfral_address() { |  | ||||||
|     if(state.cyfral_address_index < (state.cyfral_address_count - 1)) { |  | ||||||
|         state.cyfral_address_index++; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::decrease_cyfral_address() { |  | ||||||
|     if(state.cyfral_address_index > 0) { |  | ||||||
|         state.cyfral_address_index--; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButton::switch_to_mode(uint8_t mode_index) { |  | ||||||
|     mode[state.mode_index]->release(); |  | ||||||
|     state.mode_index = mode_index; |  | ||||||
|     mode[state.mode_index]->acquire(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // app enter function
 | // app enter function
 | ||||||
| extern "C" int32_t app_ibutton(void* p) { | extern "C" int32_t app_ibutton(void* p) { | ||||||
|     AppiButton* app = new AppiButton(); |     iButtonApp* app = new iButtonApp(); | ||||||
|     app->run(); |     app->run(); | ||||||
|     return 0; |     delete app; | ||||||
|  | 
 | ||||||
|  |     return 255; | ||||||
| } | } | ||||||
| @ -1,77 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "app-template.h" |  | ||||||
| #include "ibutton_mode_template.h" |  | ||||||
| 
 |  | ||||||
| // event enumeration type
 |  | ||||||
| typedef uint8_t event_t; |  | ||||||
| 
 |  | ||||||
| class AppiButtonState { |  | ||||||
| public: |  | ||||||
|     // state data
 |  | ||||||
|     static const uint8_t dallas_address_count = 3; |  | ||||||
|     uint8_t dallas_address[dallas_address_count][8] = { |  | ||||||
|         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |  | ||||||
|         {0x01, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6}, |  | ||||||
|         {0x01, 0xFD, 0x0E, 0x84, 0x01, 0x00, 0x00, 0xDB}}; |  | ||||||
| 
 |  | ||||||
|     uint8_t dallas_address_index; |  | ||||||
| 
 |  | ||||||
|     static const uint8_t cyfral_address_count = 3; |  | ||||||
|     uint8_t cyfral_address[cyfral_address_count][4] = { |  | ||||||
|         {0x00, 0x00, 0x00, 0x00}, |  | ||||||
|         {0xBB, 0xBB, 0x7B, 0xBD}, |  | ||||||
|         {0x7B, 0xDE, 0x7B, 0xDE}}; |  | ||||||
|     uint8_t cyfral_address_index; |  | ||||||
| 
 |  | ||||||
|     uint8_t mode_index; |  | ||||||
| 
 |  | ||||||
|     // state initializer
 |  | ||||||
|     AppiButtonState() { |  | ||||||
|         mode_index = 0; |  | ||||||
|         dallas_address_index = 0; |  | ||||||
|         cyfral_address_index = 0; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // events class
 |  | ||||||
| class AppiButtonEvent { |  | ||||||
| public: |  | ||||||
|     // events enum
 |  | ||||||
|     static const event_t EventTypeTick = 0; |  | ||||||
|     static const event_t EventTypeKey = 1; |  | ||||||
| 
 |  | ||||||
|     // payload
 |  | ||||||
|     union { |  | ||||||
|         InputEvent input; |  | ||||||
|     } value; |  | ||||||
| 
 |  | ||||||
|     // event type
 |  | ||||||
|     event_t type; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // our app derived from base AppTemplate class
 |  | ||||||
| // with template variables <state, events>
 |  | ||||||
| class AppiButton : public AppTemplate<AppiButtonState, AppiButtonEvent> { |  | ||||||
| public: |  | ||||||
|     const GpioPin* red_led_record; |  | ||||||
|     const GpioPin* green_led_record; |  | ||||||
| 
 |  | ||||||
|     static const uint8_t modes_count = 5; |  | ||||||
|     AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count]; |  | ||||||
| 
 |  | ||||||
|     void run(); |  | ||||||
|     void render(Canvas* canvas); |  | ||||||
|     void render_dallas_list(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void render_cyfral_list(Canvas* canvas, AppiButtonState* state); |  | ||||||
| 
 |  | ||||||
|     void blink_red(); |  | ||||||
|     void blink_green(); |  | ||||||
| 
 |  | ||||||
|     void increase_mode(); |  | ||||||
|     void decrease_mode(); |  | ||||||
|     void increase_dallas_address(); |  | ||||||
|     void decrease_dallas_address(); |  | ||||||
|     void increase_cyfral_address(); |  | ||||||
|     void decrease_cyfral_address(); |  | ||||||
|     void switch_to_mode(uint8_t mode_index); |  | ||||||
| }; |  | ||||||
| @ -1,55 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "ibutton.h" |  | ||||||
| #include "cyfral_emulator.h" |  | ||||||
| 
 |  | ||||||
| class AppiButtonModeCyfralEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { |  | ||||||
| public: |  | ||||||
|     const char* name = "cyfral emulate"; |  | ||||||
|     AppiButton* app; |  | ||||||
|     CyfralEmulator* cyfral_emulator; |  | ||||||
| 
 |  | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |  | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void acquire(); |  | ||||||
|     void release(); |  | ||||||
| 
 |  | ||||||
|     AppiButtonModeCyfralEmulate(AppiButton* parent_app) { |  | ||||||
|         app = parent_app; |  | ||||||
| 
 |  | ||||||
|         // TODO open record
 |  | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |  | ||||||
|         cyfral_emulator = new CyfralEmulator(one_wire_pin_record); |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { |  | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |  | ||||||
|         // repeat key sending 8 times
 |  | ||||||
|         cyfral_emulator->send(state->cyfral_address[state->cyfral_address_index], 4, 8); |  | ||||||
|         app->blink_green(); |  | ||||||
| 
 |  | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { |  | ||||||
|             app->decrease_cyfral_address(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { |  | ||||||
|             app->increase_cyfral_address(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralEmulate::render(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, "< Cyfral emulate"); |  | ||||||
| 
 |  | ||||||
|     app->render_cyfral_list(canvas, state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralEmulate::acquire() { |  | ||||||
|     cyfral_emulator->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralEmulate::release() { |  | ||||||
|     cyfral_emulator->stop(); |  | ||||||
| } |  | ||||||
| @ -1,85 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "ibutton.h" |  | ||||||
| #include "cyfral_reader_comp.h" |  | ||||||
| 
 |  | ||||||
| class AppiButtonModeCyfralRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { |  | ||||||
| public: |  | ||||||
|     const char* name = "cyfral read"; |  | ||||||
|     AppiButton* app; |  | ||||||
|     CyfralReaderComp* reader; |  | ||||||
| 
 |  | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |  | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void acquire(); |  | ||||||
|     void release(); |  | ||||||
| 
 |  | ||||||
|     AppiButtonModeCyfralRead(AppiButton* parent_app) { |  | ||||||
|         app = parent_app; |  | ||||||
| 
 |  | ||||||
|         // TODO open record
 |  | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |  | ||||||
|         reader = new CyfralReaderComp(one_wire_pin_record); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     static const uint8_t key_length = 4; |  | ||||||
|     static const uint8_t num_keys_to_check = 4; |  | ||||||
|     uint8_t key_index = 0; |  | ||||||
|     uint8_t keys[num_keys_to_check][4]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralRead::event(AppiButtonEvent* event, AppiButtonState* state) { |  | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |  | ||||||
|         // if we read a key
 |  | ||||||
|         if(reader->read(keys[key_index], key_length)) { |  | ||||||
|             // read next key
 |  | ||||||
|             key_index++; |  | ||||||
| 
 |  | ||||||
|             // if we read sufficient amount of keys
 |  | ||||||
|             if(key_index >= num_keys_to_check) { |  | ||||||
|                 bool result = true; |  | ||||||
|                 key_index = 0; |  | ||||||
| 
 |  | ||||||
|                 // compare all keys
 |  | ||||||
|                 for(uint8_t i = 1; i < num_keys_to_check; i++) { |  | ||||||
|                     if(memcmp(keys[i], keys[i - 1], key_length) != 0) { |  | ||||||
|                         result = false; |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // if all keys is same
 |  | ||||||
|                 if(result) { |  | ||||||
|                     // copy key to mem and blink
 |  | ||||||
|                     memcpy( |  | ||||||
|                         app->state.cyfral_address[app->state.cyfral_address_index], |  | ||||||
|                         keys[key_index], |  | ||||||
|                         key_length); |  | ||||||
|                     app->blink_green(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { |  | ||||||
|             app->decrease_cyfral_address(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { |  | ||||||
|             app->increase_cyfral_address(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralRead::render(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, "< Cyfral read >"); |  | ||||||
| 
 |  | ||||||
|     app->render_cyfral_list(canvas, state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralRead::acquire() { |  | ||||||
|     reader->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeCyfralRead::release() { |  | ||||||
|     reader->stop(); |  | ||||||
| } |  | ||||||
| @ -1,78 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "ibutton.h" |  | ||||||
| #include "one_wire_slave.h" |  | ||||||
| #include "one_wire_device_ds_1990.h" |  | ||||||
| #include "callback-connector.h" |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| class AppiButtonModeDallasEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { |  | ||||||
| private: |  | ||||||
|     void result_callback(bool success, void* ctx); |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     const char* name = "dallas emulate"; |  | ||||||
|     AppiButton* app; |  | ||||||
|     DS1990 key; |  | ||||||
|     OneWireSlave* onewire_slave; |  | ||||||
| 
 |  | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |  | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void acquire(); |  | ||||||
|     void release(); |  | ||||||
| 
 |  | ||||||
|     std::atomic<bool> emulated_result{false}; |  | ||||||
| 
 |  | ||||||
|     AppiButtonModeDallasEmulate(AppiButton* parent_app) |  | ||||||
|         : key(1, 2, 3, 4, 5, 6, 7) { |  | ||||||
|         app = parent_app; |  | ||||||
| 
 |  | ||||||
|         // TODO open record
 |  | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |  | ||||||
|         onewire_slave = new OneWireSlave(one_wire_pin_record); |  | ||||||
|         onewire_slave->attach(&key); |  | ||||||
| 
 |  | ||||||
|         auto cb = cbc::obtain_connector(this, &AppiButtonModeDallasEmulate::result_callback); |  | ||||||
|         onewire_slave->set_result_callback(cb, this); |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasEmulate::result_callback(bool success, void* ctx) { |  | ||||||
|     AppiButtonModeDallasEmulate* _this = static_cast<AppiButtonModeDallasEmulate*>(ctx); |  | ||||||
|     _this->emulated_result = success; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { |  | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |  | ||||||
|         if(emulated_result) { |  | ||||||
|             emulated_result = false; |  | ||||||
|             app->blink_green(); |  | ||||||
|         } |  | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { |  | ||||||
|             app->decrease_dallas_address(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { |  | ||||||
|             app->increase_dallas_address(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     onewire_slave->deattach(); |  | ||||||
|     memcpy(key.id_storage, state->dallas_address[state->dallas_address_index], 8); |  | ||||||
|     onewire_slave->attach(&key); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasEmulate::render(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, "< Dallas emulate >"); |  | ||||||
| 
 |  | ||||||
|     app->render_dallas_list(canvas, state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasEmulate::acquire() { |  | ||||||
|     onewire_slave->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasEmulate::release() { |  | ||||||
|     onewire_slave->stop(); |  | ||||||
| } |  | ||||||
| @ -1,71 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "ibutton.h" |  | ||||||
| #include "one_wire_master.h" |  | ||||||
| #include "maxim_crc.h" |  | ||||||
| 
 |  | ||||||
| class AppiButtonModeDallasRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { |  | ||||||
| public: |  | ||||||
|     const char* name = "dallas read"; |  | ||||||
|     AppiButton* app; |  | ||||||
|     OneWireMaster* onewire; |  | ||||||
| 
 |  | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |  | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void acquire(); |  | ||||||
|     void release(); |  | ||||||
| 
 |  | ||||||
|     AppiButtonModeDallasRead(AppiButton* parent_app) { |  | ||||||
|         app = parent_app; |  | ||||||
| 
 |  | ||||||
|         // TODO open record
 |  | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |  | ||||||
|         onewire = new OneWireMaster(one_wire_pin_record); |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* state) { |  | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |  | ||||||
|         bool result = 0; |  | ||||||
|         uint8_t address[8]; |  | ||||||
| 
 |  | ||||||
|         osKernelLock(); |  | ||||||
|         result = onewire->reset(); |  | ||||||
|         osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|         if(result) { |  | ||||||
|             osKernelLock(); |  | ||||||
|             __disable_irq(); |  | ||||||
|             onewire->write(0x33); |  | ||||||
|             onewire->read_bytes(address, 8); |  | ||||||
|             __enable_irq(); |  | ||||||
|             osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|             if(maxim_crc8(address, 8) == 0) { |  | ||||||
|                 memcpy(app->state.dallas_address[app->state.dallas_address_index], address, 8); |  | ||||||
|                 app->blink_green(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { |  | ||||||
|             app->decrease_dallas_address(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { |  | ||||||
|             app->increase_dallas_address(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasRead::render(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, "Dallas read >"); |  | ||||||
|     app->render_dallas_list(canvas, state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasRead::acquire() { |  | ||||||
|     onewire->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasRead::release() { |  | ||||||
|     onewire->stop(); |  | ||||||
| } |  | ||||||
| @ -1,64 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "ibutton.h" |  | ||||||
| #include "blanks_writer.h" |  | ||||||
| #include "maxim_crc.h" |  | ||||||
| 
 |  | ||||||
| class AppiButtonModeDallasWrite : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { |  | ||||||
| public: |  | ||||||
|     const char* name = "dallas read"; |  | ||||||
|     AppiButton* app; |  | ||||||
|     BlanksWriter* writer; |  | ||||||
| 
 |  | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |  | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |  | ||||||
|     void acquire(); |  | ||||||
|     void release(); |  | ||||||
| 
 |  | ||||||
|     const GpioPin* one_wire_pin_record; |  | ||||||
| 
 |  | ||||||
|     AppiButtonModeDallasWrite(AppiButton* parent_app) { |  | ||||||
|         app = parent_app; |  | ||||||
| 
 |  | ||||||
|         // TODO open record
 |  | ||||||
|         one_wire_pin_record = &ibutton_gpio; |  | ||||||
|         writer = new BlanksWriter(one_wire_pin_record); |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasWrite::event(AppiButtonEvent* event, AppiButtonState* state) { |  | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |  | ||||||
|         WriterResult result = |  | ||||||
|             writer->write(KEY_DS1990, state->dallas_address[state->dallas_address_index], 8); |  | ||||||
| 
 |  | ||||||
|         if(result == WR_SAME_KEY) { |  | ||||||
|             app->blink_green(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(result == WR_OK) { |  | ||||||
|             app->blink_red(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { |  | ||||||
|             app->decrease_dallas_address(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { |  | ||||||
|             app->increase_dallas_address(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasWrite::render(Canvas* canvas, AppiButtonState* state) { |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, "< Dallas write >"); |  | ||||||
|     app->render_dallas_list(canvas, state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasWrite::acquire() { |  | ||||||
|     writer->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void AppiButtonModeDallasWrite::release() { |  | ||||||
|     writer->stop(); |  | ||||||
| } |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| // template for modes
 |  | ||||||
| template <class TState, class TEvents> class AppTemplateMode { |  | ||||||
| public: |  | ||||||
|     const char* name; |  | ||||||
|     virtual void event(TEvents* event, TState* state) = 0; |  | ||||||
|     virtual void render(Canvas* canvas, TState* state) = 0; |  | ||||||
|     virtual void acquire() = 0; |  | ||||||
|     virtual void release() = 0; |  | ||||||
| }; |  | ||||||
							
								
								
									
										62
									
								
								applications/ibutton/scene/ibutton-scene-add-type.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								applications/ibutton/scene/ibutton-scene-add-type.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | #include "ibutton-scene-add-type.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SubmenuIndexCyfral, | ||||||
|  |     SubmenuIndexDallas, | ||||||
|  |     SubmenuIndexMetakom, | ||||||
|  | } SubmenuIndex; | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddType::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneAddType::submenu_callback); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, callback, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneAddType::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|  |         switch(event->payload.menu_index) { | ||||||
|  |         case SubmenuIndexCyfral: | ||||||
|  |             app->get_key()->set_type(iButtonKeyType::KeyCyfral); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexDallas: | ||||||
|  |             app->get_key()->set_type(iButtonKeyType::KeyDallas); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexMetakom: | ||||||
|  |             app->get_key()->set_type(iButtonKeyType::KeyMetakom); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddType::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view->get_submenu(); | ||||||
|  | 
 | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-add-type.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-add-type.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneAddType : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void submenu_callback(void* context, uint32_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										50
									
								
								applications/ibutton/scene/ibutton-scene-add-value.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								applications/ibutton/scene/ibutton-scene-add-value.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | #include "ibutton-scene-add-value.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddValue::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     ByteInput* byte_input = view_manager->get_byte_input(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneAddValue::byte_input_callback); | ||||||
|  | 
 | ||||||
|  |     byte_input_set_result_callback( | ||||||
|  |         byte_input, | ||||||
|  |         callback, | ||||||
|  |         NULL, | ||||||
|  |         app, | ||||||
|  |         app->get_key()->get_data(), | ||||||
|  |         app->get_key()->get_type_data_size()); | ||||||
|  |     byte_input_set_header_text(byte_input, "Enter the key"); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneAddValue::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeByteEditResult) { | ||||||
|  |         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddValue::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     ByteInput* byte_input = view_manager->get_byte_input(); | ||||||
|  | 
 | ||||||
|  |     byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); | ||||||
|  |     byte_input_set_header_text(byte_input, {0}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneAddValue::byte_input_callback(void* context, uint8_t* bytes, uint8_t bytes_count) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeByteEditResult; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-add-value.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-add-value.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneAddValue : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void byte_input_callback(void* context, uint8_t* bytes, uint8_t bytes_count); | ||||||
|  | }; | ||||||
							
								
								
									
										93
									
								
								applications/ibutton/scene/ibutton-scene-delete-confirm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								applications/ibutton/scene/ibutton-scene-delete-confirm.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | #include "ibutton-scene-delete-confirm.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteConfirm::dialog_ex_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     switch(key->get_key_type()) { | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "Delete %s?\nID: %02X %02X %02X %02X %02X %02X %02X %02X\nType: Dallas", | ||||||
|  |             key->get_name(), | ||||||
|  |             key_data[0], | ||||||
|  |             key_data[1], | ||||||
|  |             key_data[2], | ||||||
|  |             key_data[3], | ||||||
|  |             key_data[4], | ||||||
|  |             key_data[5], | ||||||
|  |             key_data[6], | ||||||
|  |             key_data[7]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "Delete %s?\nID: %02X %02X\nType: Cyfral", key->get_name(), key_data[0], key_data[1]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "Delete %s?\nID: %02X %02X %02X %02X\nType: Metakom", | ||||||
|  |             key->get_name(), | ||||||
|  |             key_data[0], | ||||||
|  |             key_data[1], | ||||||
|  |             key_data[2], | ||||||
|  |             key_data[3]); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 26, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "Delete"); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, callback); | ||||||
|  |     dialog_ex_set_context(dialog_ex, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneDeleteConfirm::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeDialogResult) { | ||||||
|  |         if(event->payload.dialog_result == DialogExResultRight) { | ||||||
|  |             KeyStore* store = app->get_key_store(); | ||||||
|  |             store->remove_key(app->get_stored_key_index()); | ||||||
|  | 
 | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteSuccess); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_previous_scene(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store(""); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_context(dialog_ex, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteConfirm::dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-delete-confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-delete-confirm.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonSceneDeleteConfirm : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void dialog_ex_callback(DialogExResult result, void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										51
									
								
								applications/ibutton/scene/ibutton-scene-delete-success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/ibutton/scene/ibutton-scene-delete-success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #include "ibutton-scene-delete-success.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteSuccess::popup_callback); | ||||||
|  | 
 | ||||||
|  |     popup_set_icon(popup, 0, 2, I_DolphinMafia_115x62); | ||||||
|  |     popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); | ||||||
|  | 
 | ||||||
|  |     popup_set_callback(popup, callback); | ||||||
|  |     popup_set_context(popup, app); | ||||||
|  |     popup_set_timeout(popup, 1500); | ||||||
|  |     popup_enable_timeout(popup); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneDeleteSuccess::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeBack) { | ||||||
|  |         app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneSavedList}); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteSuccess::on_exit(iButtonApp* app) { | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | 
 | ||||||
|  |     popup_disable_timeout(popup); | ||||||
|  |     popup_set_context(popup, NULL); | ||||||
|  |     popup_set_callback(popup, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneDeleteSuccess::popup_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-delete-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-delete-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneDeleteSuccess : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void popup_callback(void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										93
									
								
								applications/ibutton/scene/ibutton-scene-emulate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								applications/ibutton/scene/ibutton-scene-emulate.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | #include "ibutton-scene-emulate.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneEmulate::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  |     const char* key_name = key->get_name(); | ||||||
|  |     uint8_t line_count = 2; | ||||||
|  | 
 | ||||||
|  |     // check that stored key has name
 | ||||||
|  |     if(strcmp(key_name, "") != 0) { | ||||||
|  |         app->set_text_store("emulating\n%s", key_name); | ||||||
|  |         line_count = 2; | ||||||
|  |     } else { | ||||||
|  |         // if not, show key data
 | ||||||
|  |         switch(key->get_key_type()) { | ||||||
|  |         case iButtonKeyType::KeyDallas: | ||||||
|  |             app->set_text_store( | ||||||
|  |                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||||
|  |                 key_data[0], | ||||||
|  |                 key_data[1], | ||||||
|  |                 key_data[2], | ||||||
|  |                 key_data[3], | ||||||
|  |                 key_data[4], | ||||||
|  |                 key_data[5], | ||||||
|  |                 key_data[6], | ||||||
|  |                 key_data[7]); | ||||||
|  |             line_count = 3; | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyCyfral: | ||||||
|  |             app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]); | ||||||
|  |             line_count = 2; | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyMetakom: | ||||||
|  |             app->set_text_store( | ||||||
|  |                 "emulating\n%02X %02X %02X %02X", | ||||||
|  |                 key_data[0], | ||||||
|  |                 key_data[1], | ||||||
|  |                 key_data[2], | ||||||
|  |                 key_data[3]); | ||||||
|  |             line_count = 2; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(line_count) { | ||||||
|  |     case 3: | ||||||
|  |         popup_set_header(popup, "iButton", 92, 18, AlignCenter, AlignBottom); | ||||||
|  |         popup_set_text(popup, app->get_text_store(), 92, 22, AlignCenter, AlignTop); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         popup_set_header(popup, "iButton", 92, 24, AlignCenter, AlignBottom); | ||||||
|  |         popup_set_text(popup, app->get_text_store(), 92, 28, AlignCenter, AlignTop); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     popup_set_icon(popup, 10, 10, I_iButtonKey_49x44); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  |     app->get_key_worker()->start_emulate(app->get_key()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|  |         consumed = true; | ||||||
|  |         if(app->get_key_worker()->emulated()) { | ||||||
|  |             app->notify_yellow_blink(); | ||||||
|  |         } else { | ||||||
|  |             app->notify_red_blink(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneEmulate::on_exit(iButtonApp* app) { | ||||||
|  |     app->get_key_worker()->stop_emulate(); | ||||||
|  | 
 | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								applications/ibutton/scene/ibutton-scene-emulate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/ibutton/scene/ibutton-scene-emulate.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include "../helpers/key-emulator.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneEmulate : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | }; | ||||||
							
								
								
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-generic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-generic.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | 
 | ||||||
|  | class iButtonApp; | ||||||
|  | 
 | ||||||
|  | class iButtonScene { | ||||||
|  | public: | ||||||
|  |     virtual void on_enter(iButtonApp* app) = 0; | ||||||
|  |     virtual bool on_event(iButtonApp* app, iButtonEvent* event) = 0; | ||||||
|  |     virtual void on_exit(iButtonApp* app) = 0; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | }; | ||||||
							
								
								
									
										82
									
								
								applications/ibutton/scene/ibutton-scene-info.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								applications/ibutton/scene/ibutton-scene-info.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | #include "ibutton-scene-info.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneInfo::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneInfo::dialog_ex_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     switch(key->get_key_type()) { | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "%s\n%02X %02X %02X %02X %02X %02X %02X %02X\nDallas", | ||||||
|  |             key->get_name(), | ||||||
|  |             key_data[0], | ||||||
|  |             key_data[1], | ||||||
|  |             key_data[2], | ||||||
|  |             key_data[3], | ||||||
|  |             key_data[4], | ||||||
|  |             key_data[5], | ||||||
|  |             key_data[6], | ||||||
|  |             key_data[7]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "%s\n%02X %02X %02X %02X\nMetakom", | ||||||
|  |             key->get_name(), | ||||||
|  |             key_data[0], | ||||||
|  |             key_data[1], | ||||||
|  |             key_data[2], | ||||||
|  |             key_data[3]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         app->set_text_store("%s\n%02X %02X\nCyfral", key->get_name(), key_data[0], key_data[1]); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 26, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, callback); | ||||||
|  |     dialog_ex_set_context(dialog_ex, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneInfo::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeDialogResult) { | ||||||
|  |         app->switch_to_previous_scene(); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneInfo::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store(""); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_context(dialog_ex, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneInfo::dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-info.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonSceneInfo : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void dialog_ex_callback(DialogExResult result, void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										75
									
								
								applications/ibutton/scene/ibutton-scene-read-crc-error.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								applications/ibutton/scene/ibutton-scene-read-crc-error.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | |||||||
|  | #include "ibutton-scene-read-crc-error.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadCRCError::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneReadCRCError::dialog_ex_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store( | ||||||
|  |         "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X", | ||||||
|  |         key_data[0], | ||||||
|  |         key_data[1], | ||||||
|  |         key_data[2], | ||||||
|  |         key_data[3], | ||||||
|  |         key_data[4], | ||||||
|  |         key_data[5], | ||||||
|  |         key_data[6], | ||||||
|  |         key_data[7], | ||||||
|  |         maxim_crc8(key_data, 7)); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, callback); | ||||||
|  |     dialog_ex_set_context(dialog_ex, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
|  |     app->notify_error(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneReadCRCError::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeDialogResult) { | ||||||
|  |         if(event->payload.dialog_result == DialogExResultRight) { | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_previous_scene(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadCRCError::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store(""); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_context(dialog_ex, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadCRCError::dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-read-crc-error.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-read-crc-error.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonSceneReadCRCError : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void dialog_ex_callback(DialogExResult result, void* context); | ||||||
|  | }; | ||||||
| @ -0,0 +1,75 @@ | |||||||
|  | #include "ibutton-scene-read-not-key-error.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneReadNotKeyError::dialog_ex_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store( | ||||||
|  |         "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X", | ||||||
|  |         key_data[0], | ||||||
|  |         key_data[1], | ||||||
|  |         key_data[2], | ||||||
|  |         key_data[3], | ||||||
|  |         key_data[4], | ||||||
|  |         key_data[5], | ||||||
|  |         key_data[6], | ||||||
|  |         key_data[7], | ||||||
|  |         maxim_crc8(key_data, 7)); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header(dialog_ex, "ERROR:", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, callback); | ||||||
|  |     dialog_ex_set_context(dialog_ex, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
|  |     app->notify_error(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneReadNotKeyError::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeDialogResult) { | ||||||
|  |         if(event->payload.dialog_result == DialogExResultRight) { | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_previous_scene(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadNotKeyError::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store(""); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_context(dialog_ex, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadNotKeyError::dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonSceneReadNotKeyError : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void dialog_ex_callback(DialogExResult result, void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										88
									
								
								applications/ibutton/scene/ibutton-scene-read-success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								applications/ibutton/scene/ibutton-scene-read-success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | |||||||
|  | #include "ibutton-scene-read-success.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneReadSuccess::dialog_ex_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  | 
 | ||||||
|  |     switch(key->get_key_type()) { | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "%02X %02X %02X %02X\n%02X %02X %02X %02X\nDallas", | ||||||
|  |             key_data[0], | ||||||
|  |             key_data[1], | ||||||
|  |             key_data[2], | ||||||
|  |             key_data[3], | ||||||
|  |             key_data[4], | ||||||
|  |             key_data[5], | ||||||
|  |             key_data[6], | ||||||
|  |             key_data[7]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         app->set_text_store("%02X %02X\nCyfral", key_data[0], key_data[1]); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         app->set_text_store( | ||||||
|  |             "%02X %02X %02X %02X\nMetakom", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 95, 30, AlignCenter, AlignCenter); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|  |     dialog_ex_set_icon(dialog_ex, 0, 1, I_DolphinExcited_64x63); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, callback); | ||||||
|  |     dialog_ex_set_context(dialog_ex, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
|  |     app->notify_green_on(); | ||||||
|  |     app->notify_success(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneReadSuccess::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeDialogResult) { | ||||||
|  |         if(event->payload.dialog_result == DialogExResultRight) { | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadedKeyMenu); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_previous_scene(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadSuccess::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  | 
 | ||||||
|  |     app->set_text_store(""); | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_context(dialog_ex, NULL); | ||||||
|  |     dialog_ex_set_icon(dialog_ex, -1, -1, I_ButtonCenter_7x7); | ||||||
|  |     app->notify_green_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadSuccess::dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-read-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/ibutton/scene/ibutton-scene-read-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | class iButtonSceneReadSuccess : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void dialog_ex_callback(DialogExResult result, void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										53
									
								
								applications/ibutton/scene/ibutton-scene-read.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								applications/ibutton/scene/ibutton-scene-read.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | #include "ibutton-scene-read.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | 
 | ||||||
|  | void iButtonSceneRead::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, "waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, 0, 5, I_DolphinWait_61x59); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  |     app->get_key()->set_name(""); | ||||||
|  | 
 | ||||||
|  |     app->get_key_worker()->start_read(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|  |         consumed = true; | ||||||
|  |         app->notify_red_blink(); | ||||||
|  | 
 | ||||||
|  |         switch(app->get_key_worker()->read(app->get_key())) { | ||||||
|  |         case KeyReader::Error::EMPTY: | ||||||
|  |             break; | ||||||
|  |         case KeyReader::Error::OK: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); | ||||||
|  |             break; | ||||||
|  |         case KeyReader::Error::CRC_ERROR: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError); | ||||||
|  |             break; | ||||||
|  |         case KeyReader::Error::NOT_ARE_KEY: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneRead::on_exit(iButtonApp* app) { | ||||||
|  |     app->get_key_worker()->stop_read(); | ||||||
|  | 
 | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								applications/ibutton/scene/ibutton-scene-read.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/ibutton/scene/ibutton-scene-read.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneRead : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | }; | ||||||
							
								
								
									
										64
									
								
								applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | #include "ibutton-scene-readed-key-menu.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SubmenuIndexWrite, | ||||||
|  |     SubmenuIndexEmulate, | ||||||
|  |     SubmenuIndexNameAndSave, | ||||||
|  | } SubmenuIndex; | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneReadedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|  |         switch(event->payload.menu_index) { | ||||||
|  |         case SubmenuIndexWrite: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneWrite); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexEmulate: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexNameAndSave: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } else if(event->type == iButtonEvent::Type::EventTypeBack) { | ||||||
|  |         app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneRead}); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view->get_submenu(); | ||||||
|  | 
 | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-readed-key-menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-readed-key-menu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneReadedKeyMenu : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void submenu_callback(void* context, uint32_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										59
									
								
								applications/ibutton/scene/ibutton-scene-save-name.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/ibutton/scene/ibutton-scene-save-name.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | #include "ibutton-scene-save-name.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveName::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     TextInput* text_input = view_manager->get_text_input(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneSaveName::text_input_callback); | ||||||
|  | 
 | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     const char* key_name = key->get_name(); | ||||||
|  | 
 | ||||||
|  |     if(strcmp(key_name, "") == 0) { | ||||||
|  |         app->generate_random_name(app->get_text_store(), app->get_text_store_size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     text_input_set_header_text(text_input, "Name the key"); | ||||||
|  |     text_input_set_result_callback( | ||||||
|  |         text_input, callback, app, app->get_text_store(), app->get_text_store_size()); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeTextEditResult) { | ||||||
|  |         KeyStore* store = app->get_key_store(); | ||||||
|  |         uint8_t key_index = store->add_key(); | ||||||
|  |         iButtonKey* key = app->get_key(); | ||||||
|  | 
 | ||||||
|  |         store->set_key_type(key_index, key->get_key_type()); | ||||||
|  |         store->set_key_name(key_index, app->get_text_store()); | ||||||
|  |         store->set_key_data(key_index, key->get_data(), key->get_size()); | ||||||
|  | 
 | ||||||
|  |         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveName::on_exit(iButtonApp* app) { | ||||||
|  |     TextInput* text_input = app->get_view_manager()->get_text_input(); | ||||||
|  |     text_input_set_header_text(text_input, ""); | ||||||
|  |     text_input_set_result_callback(text_input, NULL, NULL, NULL, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveName::text_input_callback(void* context, char* text) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeTextEditResult; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-save-name.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-save-name.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneSaveName : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void text_input_callback(void* context, char* text); | ||||||
|  | }; | ||||||
							
								
								
									
										54
									
								
								applications/ibutton/scene/ibutton-scene-save-success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/ibutton/scene/ibutton-scene-save-success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | #include "ibutton-scene-save-success.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneSaveSuccess::popup_callback); | ||||||
|  | 
 | ||||||
|  |     popup_set_icon(popup, 32, 5, I_DolphinNice_96x59); | ||||||
|  |     popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); | ||||||
|  | 
 | ||||||
|  |     popup_set_callback(popup, callback); | ||||||
|  |     popup_set_context(popup, app); | ||||||
|  |     popup_set_timeout(popup, 1500); | ||||||
|  |     popup_enable_timeout(popup); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneSaveSuccess::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeBack) { | ||||||
|  |         app->search_and_switch_to_previous_scene( | ||||||
|  |             {iButtonApp::Scene::SceneReadedKeyMenu, | ||||||
|  |              iButtonApp::Scene::SceneSavedKeyMenu, | ||||||
|  |              iButtonApp::Scene::SceneAddType}); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveSuccess::on_exit(iButtonApp* app) { | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | 
 | ||||||
|  |     popup_disable_timeout(popup); | ||||||
|  |     popup_set_context(popup, NULL); | ||||||
|  |     popup_set_callback(popup, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSaveSuccess::popup_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-save-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-save-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneSaveSuccess : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void popup_callback(void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										71
									
								
								applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | #include "ibutton-scene-saved-key-menu.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SubmenuIndexWrite, | ||||||
|  |     SubmenuIndexEmulate, | ||||||
|  |     SubmenuIndexEdit, | ||||||
|  |     SubmenuIndexDelete, | ||||||
|  |     SubmenuIndexInfo, | ||||||
|  | } SubmenuIndex; | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneSavedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|  |         switch(event->payload.menu_index) { | ||||||
|  |         case SubmenuIndexWrite: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneWrite); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexEmulate: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexEdit: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexDelete: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteConfirm); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexInfo: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneInfo); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view->get_submenu(); | ||||||
|  | 
 | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-saved-key-menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-saved-key-menu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneSavedKeyMenu : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void submenu_callback(void* context, uint32_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										62
									
								
								applications/ibutton/scene/ibutton-scene-saved.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								applications/ibutton/scene/ibutton-scene-saved.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | #include "ibutton-scene-saved.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedList::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedList::submenu_callback); | ||||||
|  | 
 | ||||||
|  |     KeyStore* store = app->get_key_store(); | ||||||
|  | 
 | ||||||
|  |     if(store->get_key_count() > 0) { | ||||||
|  |         for(uint8_t i = 0; i < store->get_key_count(); i++) { | ||||||
|  |             submenu_add_item(submenu, store->get_key_name(i), i, callback, app); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         submenu_add_item(submenu, "Empty", 0, NULL, NULL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneSavedList::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|  |         uint8_t key_index = event->payload.menu_index; | ||||||
|  | 
 | ||||||
|  |         // store data
 | ||||||
|  |         iButtonKey* stored_key_data = app->get_key(); | ||||||
|  |         KeyStore* store = app->get_key_store(); | ||||||
|  | 
 | ||||||
|  |         app->set_stored_key_index(key_index); | ||||||
|  |         stored_key_data->set_name(store->get_key_name(key_index)); | ||||||
|  |         stored_key_data->set_type(store->get_key_type(key_index)); | ||||||
|  |         stored_key_data->set_data(store->get_key_data(key_index), stored_key_data->get_size()); | ||||||
|  | 
 | ||||||
|  |         app->switch_to_next_scene(iButtonApp::Scene::SceneSavedKeyMenu); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedList::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view->get_submenu(); | ||||||
|  | 
 | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneSavedList::submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-saved.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-saved.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneSavedList : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void submenu_callback(void* context, uint32_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										61
									
								
								applications/ibutton/scene/ibutton-scene-start.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								applications/ibutton/scene/ibutton-scene-start.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | #include "ibutton-scene-start.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SubmenuIndexRead, | ||||||
|  |     SubmenuIndexSaved, | ||||||
|  |     SubmenuIndexAdd, | ||||||
|  | } SubmenuIndex; | ||||||
|  | 
 | ||||||
|  | void iButtonSceneStart::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneStart::submenu_callback); | ||||||
|  | 
 | ||||||
|  |     submenu_add_item(submenu, "Read", SubmenuIndexRead, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, callback, app); | ||||||
|  |     submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, callback, app); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneStart::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|  |         switch(event->payload.menu_index) { | ||||||
|  |         case SubmenuIndexRead: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneRead); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexSaved: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneSavedList); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexAdd: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneAddType); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneStart::on_exit(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view = app->get_view_manager(); | ||||||
|  |     Submenu* submenu = view->get_submenu(); | ||||||
|  | 
 | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneStart::submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-start.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-start.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneStart : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void submenu_callback(void* context, uint32_t index); | ||||||
|  | }; | ||||||
							
								
								
									
										55
									
								
								applications/ibutton/scene/ibutton-scene-write-success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/ibutton/scene/ibutton-scene-write-success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #include "ibutton-scene-write-success.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | #include <callback-connector.h> | ||||||
|  | 
 | ||||||
|  | void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     auto callback = cbc::obtain_connector(this, &iButtonSceneWriteSuccess::popup_callback); | ||||||
|  | 
 | ||||||
|  |     popup_set_icon(popup, 0, 12, I_iButtonDolphinVerySuccess_108x52); | ||||||
|  |     popup_set_text(popup, "Successful writing!", 47, 14, AlignLeft, AlignBottom); | ||||||
|  | 
 | ||||||
|  |     popup_set_callback(popup, callback); | ||||||
|  |     popup_set_context(popup, app); | ||||||
|  |     popup_set_timeout(popup, 1500); | ||||||
|  |     popup_enable_timeout(popup); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  |     app->notify_green_on(); | ||||||
|  |     app->notify_success(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneWriteSuccess::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeBack) { | ||||||
|  |         app->search_and_switch_to_previous_scene( | ||||||
|  |             {iButtonApp::Scene::SceneReadedKeyMenu, iButtonApp::Scene::SceneStart}); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneWriteSuccess::on_exit(iButtonApp* app) { | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | 
 | ||||||
|  |     popup_disable_timeout(popup); | ||||||
|  |     popup_set_context(popup, NULL); | ||||||
|  |     popup_set_callback(popup, NULL); | ||||||
|  |     app->notify_green_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneWriteSuccess::popup_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-write-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/ibutton/scene/ibutton-scene-write-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneWriteSuccess : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void popup_callback(void* context); | ||||||
|  | }; | ||||||
							
								
								
									
										98
									
								
								applications/ibutton/scene/ibutton-scene-write.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								applications/ibutton/scene/ibutton-scene-write.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | #include "ibutton-scene-write.h" | ||||||
|  | #include "../ibutton-app.h" | ||||||
|  | #include "../ibutton-view-manager.h" | ||||||
|  | #include "../ibutton-event.h" | ||||||
|  | #include "../ibutton-key.h" | ||||||
|  | 
 | ||||||
|  | void iButtonSceneWrite::on_enter(iButtonApp* app) { | ||||||
|  |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     uint8_t* key_data = key->get_data(); | ||||||
|  |     const char* key_name = key->get_name(); | ||||||
|  |     uint8_t line_count = 2; | ||||||
|  | 
 | ||||||
|  |     // check that stored key has name
 | ||||||
|  |     if(strcmp(key_name, "") != 0) { | ||||||
|  |         app->set_text_store("writing\n%s", key_name); | ||||||
|  |         line_count = 2; | ||||||
|  |     } else { | ||||||
|  |         // if not, show key data
 | ||||||
|  |         switch(key->get_key_type()) { | ||||||
|  |         case iButtonKeyType::KeyDallas: | ||||||
|  |             app->set_text_store( | ||||||
|  |                 "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||||
|  |                 key_data[0], | ||||||
|  |                 key_data[1], | ||||||
|  |                 key_data[2], | ||||||
|  |                 key_data[3], | ||||||
|  |                 key_data[4], | ||||||
|  |                 key_data[5], | ||||||
|  |                 key_data[6], | ||||||
|  |                 key_data[7]); | ||||||
|  |             line_count = 3; | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyCyfral: | ||||||
|  |             app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]); | ||||||
|  |             line_count = 2; | ||||||
|  |             break; | ||||||
|  |         case iButtonKeyType::KeyMetakom: | ||||||
|  |             app->set_text_store( | ||||||
|  |                 "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|  |             line_count = 2; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(line_count) { | ||||||
|  |     case 3: | ||||||
|  |         popup_set_header(popup, "iButton", 92, 18, AlignCenter, AlignBottom); | ||||||
|  |         popup_set_text(popup, app->get_text_store(), 92, 22, AlignCenter, AlignTop); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         popup_set_header(popup, "iButton", 92, 24, AlignCenter, AlignBottom); | ||||||
|  |         popup_set_text(popup, app->get_text_store(), 92, 28, AlignCenter, AlignTop); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     popup_set_icon(popup, 10, 10, I_iButtonKey_49x44); | ||||||
|  | 
 | ||||||
|  |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|  | 
 | ||||||
|  |     app->get_key_worker()->start_write(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|  |         consumed = true; | ||||||
|  |         KeyWriter::Error result = app->get_key_worker()->write(app->get_key()); | ||||||
|  | 
 | ||||||
|  |         switch(result) { | ||||||
|  |         case KeyWriter::Error::SAME_KEY: | ||||||
|  |         case KeyWriter::Error::OK: | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess); | ||||||
|  |             break; | ||||||
|  |         case KeyWriter::Error::NO_DETECT: | ||||||
|  |             app->notify_red_blink(); | ||||||
|  |             break; | ||||||
|  |         case KeyWriter::Error::CANNOT_WRITE: | ||||||
|  |             app->notify_yellow_blink(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonSceneWrite::on_exit(iButtonApp* app) { | ||||||
|  |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|  | 
 | ||||||
|  |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_icon(popup, -1, -1, I_DolphinWait_61x59); | ||||||
|  | 
 | ||||||
|  |     app->get_key_worker()->stop_write(); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								applications/ibutton/scene/ibutton-scene-write.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/ibutton/scene/ibutton-scene-write.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton-scene-generic.h" | ||||||
|  | 
 | ||||||
|  | class iButtonSceneWrite : public iButtonScene { | ||||||
|  | public: | ||||||
|  |     void on_enter(iButtonApp* app) final; | ||||||
|  |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|  |     void on_exit(iButtonApp* app) final; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | }; | ||||||
| @ -397,7 +397,7 @@ bool fs_dir_open(File* file, const char* path) { | |||||||
|     if(sd_dir == NULL) { |     if(sd_dir == NULL) { | ||||||
|         file->internal_error_id = SD_TOO_MANY_OPEN_FILES; |         file->internal_error_id = SD_TOO_MANY_OPEN_FILES; | ||||||
|     } else { |     } else { | ||||||
|         if(file->internal_error_id == SD_OK) file->internal_error_id = f_opendir(sd_dir, path); |         file->internal_error_id = f_opendir(sd_dir, path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO on exit
 |     // TODO on exit
 | ||||||
| @ -447,7 +447,7 @@ bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, const uint16_t name | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(name != NULL && name_length > 0) { |         if(name != NULL && name_length > 0) { | ||||||
|             strncpy(name, _fileinfo.fname, name_length); |             strlcpy(name, _fileinfo.fname, name_length); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -492,7 +492,7 @@ fs_common_info(const char* path, FileInfo* fileinfo, char* name, const uint16_t | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(name != NULL && name_length > 0) { |             if(name != NULL && name_length > 0) { | ||||||
|                 strncpy(name, _fileinfo.fname, name_length); |                 strlcpy(name, _fileinfo.fname, name_length); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/Common/ButtonLeftSmall_3x5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/Common/ButtonLeftSmall_3x5.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/Common/ButtonRightSmall_3x5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/Common/ButtonRightSmall_3x5.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/iButton/DolphinNice_96x59.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/iButton/DolphinNice_96x59.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.4 KiB | 
| @ -2,10 +2,18 @@ | |||||||
| #include "main.h" | #include "main.h" | ||||||
| #include "stdbool.h" | #include "stdbool.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| 
 | 
 | ||||||
| void irda_pwm_set(float value, float freq); | void irda_pwm_set(float value, float freq); | ||||||
| void irda_pwm_stop(); | void irda_pwm_stop(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -4,23 +4,14 @@ | |||||||
| 
 | 
 | ||||||
| const InputPin input_pins[] = { | const InputPin input_pins[] = { | ||||||
|     {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true}, |     {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true}, | ||||||
|     {.port = BUTTON_DOWN_GPIO_Port, |     {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true}, | ||||||
|      .pin = BUTTON_DOWN_Pin, |  | ||||||
|      .key = InputKeyDown, |  | ||||||
|      .inverted = true}, |  | ||||||
|     {.port = BUTTON_RIGHT_GPIO_Port, |     {.port = BUTTON_RIGHT_GPIO_Port, | ||||||
|      .pin = BUTTON_RIGHT_Pin, |      .pin = BUTTON_RIGHT_Pin, | ||||||
|      .key = InputKeyRight, |      .key = InputKeyRight, | ||||||
|      .inverted = true}, |      .inverted = true}, | ||||||
|     {.port = BUTTON_LEFT_GPIO_Port, |     {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true}, | ||||||
|      .pin = BUTTON_LEFT_Pin, |  | ||||||
|      .key = InputKeyLeft, |  | ||||||
|      .inverted = true}, |  | ||||||
|     {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false}, |     {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false}, | ||||||
|     {.port = BUTTON_BACK_GPIO_Port, |     {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true}, | ||||||
|      .pin = BUTTON_BACK_Pin, |  | ||||||
|      .key = InputKeyBack, |  | ||||||
|      .inverted = true}, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); | const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); | ||||||
| @ -35,3 +26,13 @@ const GpioPin sd_cs_gpio = {SD_CS_GPIO_Port, SD_CS_Pin}; | |||||||
| const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin}; | const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin}; | ||||||
| const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin}; | const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin}; | ||||||
| const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin}; | const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin}; | ||||||
|  | 
 | ||||||
|  | // external gpio's
 | ||||||
|  | const GpioPin ext_pc0_gpio = {GPIOC, GPIO_PIN_0}; | ||||||
|  | const GpioPin ext_pc1_gpio = {GPIOC, GPIO_PIN_1}; | ||||||
|  | const GpioPin ext_pc3_gpio = {GPIOC, GPIO_PIN_3}; | ||||||
|  | const GpioPin ext_pb2_gpio = {GPIOB, GPIO_PIN_2}; | ||||||
|  | const GpioPin ext_pb3_gpio = {GPIOB, GPIO_PIN_3}; | ||||||
|  | const GpioPin ext_pa4_gpio = {GPIOA, GPIO_PIN_4}; | ||||||
|  | const GpioPin ext_pa6_gpio = {GPIOA, GPIO_PIN_6}; | ||||||
|  | const GpioPin ext_pa7_gpio = {GPIOA, GPIO_PIN_7}; | ||||||
| @ -58,6 +58,16 @@ extern const GpioPin vibro_gpio; | |||||||
| extern const GpioPin ibutton_gpio; | extern const GpioPin ibutton_gpio; | ||||||
| extern const GpioPin cc1101_g0_gpio; | extern const GpioPin cc1101_g0_gpio; | ||||||
| 
 | 
 | ||||||
|  | // external gpio's
 | ||||||
|  | extern const GpioPin ext_pc0_gpio; | ||||||
|  | extern const GpioPin ext_pc1_gpio; | ||||||
|  | extern const GpioPin ext_pc3_gpio; | ||||||
|  | extern const GpioPin ext_pb2_gpio; | ||||||
|  | extern const GpioPin ext_pb3_gpio; | ||||||
|  | extern const GpioPin ext_pa4_gpio; | ||||||
|  | extern const GpioPin ext_pa6_gpio; | ||||||
|  | extern const GpioPin ext_pa7_gpio; | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -2,10 +2,18 @@ | |||||||
| #include "main.h" | #include "main.h" | ||||||
| #include "stdbool.h" | #include "stdbool.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||||
| 
 | 
 | ||||||
| void irda_pwm_set(float value, float freq); | void irda_pwm_set(float value, float freq); | ||||||
| void irda_pwm_stop(); | void irda_pwm_stop(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -60,6 +60,12 @@ C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | |||||||
| # we build iButton application
 | # we build iButton application
 | ||||||
| ifeq ($(BUILD_IBUTTON), 1) | ifeq ($(BUILD_IBUTTON), 1) | ||||||
| # onewire library
 | # onewire library
 | ||||||
|  | BUILD_ONEWIRE	= 1 | ||||||
|  | endif | ||||||
|  | 
 | ||||||
|  | BUILD_ONEWIRE ?= 0 | ||||||
|  | ifeq ($(BUILD_ONEWIRE), 1) | ||||||
|  | # onewire library
 | ||||||
| ONEWIRE_DIR		= $(LIB_DIR)/onewire | ONEWIRE_DIR		= $(LIB_DIR)/onewire | ||||||
| CFLAGS			+= -I$(ONEWIRE_DIR) | CFLAGS			+= -I$(ONEWIRE_DIR) | ||||||
| CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp) | CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp) | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| 
 | 
 | ||||||
| OneWireMaster::OneWireMaster(const GpioPin* one_wire_gpio) { | OneWireMaster::OneWireMaster(const GpioPin* one_wire_gpio) { | ||||||
|     gpio = one_wire_gpio; |     gpio = one_wire_gpio; | ||||||
|  |     reset_search(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| OneWireMaster::~OneWireMaster() { | OneWireMaster::~OneWireMaster() { | ||||||
| @ -17,6 +18,137 @@ void OneWireMaster::stop(void) { | |||||||
|     gpio_init(gpio, GpioModeAnalog); |     gpio_init(gpio, GpioModeAnalog); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void OneWireMaster::reset_search() { | ||||||
|  |     // reset the search state
 | ||||||
|  |     last_discrepancy = 0; | ||||||
|  |     last_device_flag = false; | ||||||
|  |     last_family_discrepancy = 0; | ||||||
|  |     for(int i = 7;; i--) { | ||||||
|  |         saved_rom[i] = 0; | ||||||
|  |         if(i == 0) break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void OneWireMaster::target_search(uint8_t family_code) { | ||||||
|  |     // set the search state to find SearchFamily type devices
 | ||||||
|  |     saved_rom[0] = family_code; | ||||||
|  |     for(uint8_t i = 1; i < 8; i++) saved_rom[i] = 0; | ||||||
|  |     last_discrepancy = 64; | ||||||
|  |     last_family_discrepancy = 0; | ||||||
|  |     last_device_flag = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t OneWireMaster::search(uint8_t* newAddr, bool search_mode) { | ||||||
|  |     uint8_t id_bit_number; | ||||||
|  |     uint8_t last_zero, rom_byte_number, search_result; | ||||||
|  |     uint8_t id_bit, cmp_id_bit; | ||||||
|  | 
 | ||||||
|  |     unsigned char rom_byte_mask, search_direction; | ||||||
|  | 
 | ||||||
|  |     // initialize for search
 | ||||||
|  |     id_bit_number = 1; | ||||||
|  |     last_zero = 0; | ||||||
|  |     rom_byte_number = 0; | ||||||
|  |     rom_byte_mask = 1; | ||||||
|  |     search_result = 0; | ||||||
|  | 
 | ||||||
|  |     // if the last call was not the last one
 | ||||||
|  |     if(!last_device_flag) { | ||||||
|  |         // 1-Wire reset
 | ||||||
|  |         if(!reset()) { | ||||||
|  |             // reset the search
 | ||||||
|  |             last_discrepancy = 0; | ||||||
|  |             last_device_flag = false; | ||||||
|  |             last_family_discrepancy = 0; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // issue the search command
 | ||||||
|  |         if(search_mode == true) { | ||||||
|  |             write(0xF0); // NORMAL SEARCH
 | ||||||
|  |         } else { | ||||||
|  |             write(0xEC); // CONDITIONAL SEARCH
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // loop to do the search
 | ||||||
|  |         do { | ||||||
|  |             // read a bit and its complement
 | ||||||
|  |             id_bit = read_bit(); | ||||||
|  |             cmp_id_bit = read_bit(); | ||||||
|  | 
 | ||||||
|  |             // check for no devices on 1-wire
 | ||||||
|  |             if((id_bit == 1) && (cmp_id_bit == 1)) | ||||||
|  |                 break; | ||||||
|  |             else { | ||||||
|  |                 // all devices coupled have 0 or 1
 | ||||||
|  |                 if(id_bit != cmp_id_bit) | ||||||
|  |                     search_direction = id_bit; // bit write value for search
 | ||||||
|  |                 else { | ||||||
|  |                     // if this discrepancy if before the Last Discrepancy
 | ||||||
|  |                     // on a previous next then pick the same as last time
 | ||||||
|  |                     if(id_bit_number < last_discrepancy) | ||||||
|  |                         search_direction = ((saved_rom[rom_byte_number] & rom_byte_mask) > 0); | ||||||
|  |                     else | ||||||
|  |                         // if equal to last pick 1, if not then pick 0
 | ||||||
|  |                         search_direction = (id_bit_number == last_discrepancy); | ||||||
|  | 
 | ||||||
|  |                     // if 0 was picked then record its position in LastZero
 | ||||||
|  |                     if(search_direction == 0) { | ||||||
|  |                         last_zero = id_bit_number; | ||||||
|  | 
 | ||||||
|  |                         // check for Last discrepancy in family
 | ||||||
|  |                         if(last_zero < 9) last_family_discrepancy = last_zero; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // set or clear the bit in the ROM byte rom_byte_number
 | ||||||
|  |                 // with mask rom_byte_mask
 | ||||||
|  |                 if(search_direction == 1) | ||||||
|  |                     saved_rom[rom_byte_number] |= rom_byte_mask; | ||||||
|  |                 else | ||||||
|  |                     saved_rom[rom_byte_number] &= ~rom_byte_mask; | ||||||
|  | 
 | ||||||
|  |                 // serial number search direction write bit
 | ||||||
|  |                 write_bit(search_direction); | ||||||
|  | 
 | ||||||
|  |                 // increment the byte counter id_bit_number
 | ||||||
|  |                 // and shift the mask rom_byte_mask
 | ||||||
|  |                 id_bit_number++; | ||||||
|  |                 rom_byte_mask <<= 1; | ||||||
|  | 
 | ||||||
|  |                 // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
 | ||||||
|  |                 if(rom_byte_mask == 0) { | ||||||
|  |                     rom_byte_number++; | ||||||
|  |                     rom_byte_mask = 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
 | ||||||
|  | 
 | ||||||
|  |         // if the search was successful then
 | ||||||
|  |         if(!(id_bit_number < 65)) { | ||||||
|  |             // search successful so set last_Discrepancy, last_device_flag, search_result
 | ||||||
|  |             last_discrepancy = last_zero; | ||||||
|  | 
 | ||||||
|  |             // check for last device
 | ||||||
|  |             if(last_discrepancy == 0) last_device_flag = true; | ||||||
|  | 
 | ||||||
|  |             search_result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // if no device found then reset counters so next 'search' will be like a first
 | ||||||
|  |     if(!search_result || !saved_rom[0]) { | ||||||
|  |         last_discrepancy = 0; | ||||||
|  |         last_device_flag = false; | ||||||
|  |         last_family_discrepancy = 0; | ||||||
|  |         search_result = false; | ||||||
|  |     } else { | ||||||
|  |         for(int i = 0; i < 8; i++) newAddr[i] = saved_rom[i]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return search_result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool OneWireMaster::reset(void) { | bool OneWireMaster::reset(void) { | ||||||
|     uint8_t r; |     uint8_t r; | ||||||
|     uint8_t retries = 125; |     uint8_t retries = 125; | ||||||
|  | |||||||
| @ -7,6 +7,12 @@ class OneWireMaster { | |||||||
| private: | private: | ||||||
|     const GpioPin* gpio; |     const GpioPin* gpio; | ||||||
| 
 | 
 | ||||||
|  |     // global search state
 | ||||||
|  |     unsigned char saved_rom[8]; | ||||||
|  |     uint8_t last_discrepancy; | ||||||
|  |     uint8_t last_family_discrepancy; | ||||||
|  |     bool last_device_flag; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     OneWireMaster(const GpioPin* one_wire_gpio); |     OneWireMaster(const GpioPin* one_wire_gpio); | ||||||
|     ~OneWireMaster(); |     ~OneWireMaster(); | ||||||
| @ -19,4 +25,8 @@ public: | |||||||
|     void skip(void); |     void skip(void); | ||||||
|     void start(void); |     void start(void); | ||||||
|     void stop(void); |     void stop(void); | ||||||
|  | 
 | ||||||
|  |     void reset_search(); | ||||||
|  |     void target_search(uint8_t family_code); | ||||||
|  |     uint8_t search(uint8_t* newAddr, bool search_mode = true); | ||||||
| }; | }; | ||||||
| @ -43,8 +43,10 @@ void OneWireSlave::attach(OneWireDevice* attached_device) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireSlave::deattach(void) { | void OneWireSlave::deattach(void) { | ||||||
|  |     if(device != nullptr) { | ||||||
|  |         device->deattach(); | ||||||
|  |     } | ||||||
|     device = nullptr; |     device = nullptr; | ||||||
|     device->deattach(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireSlave::set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx) { | void OneWireSlave::set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx) { | ||||||
| @ -237,10 +239,11 @@ bool OneWireSlave::receive_and_process_cmd(void) { | |||||||
|         cmd_search_rom(); |         cmd_search_rom(); | ||||||
|         return true; |         return true; | ||||||
| 
 | 
 | ||||||
|  |     case 0x0F: | ||||||
|     case 0x33: |     case 0x33: | ||||||
|         // READ ROM
 |         // READ ROM
 | ||||||
|         device->send_id(); |         device->send_id(); | ||||||
|         return false; |         return true; | ||||||
| 
 | 
 | ||||||
|     default: // Unknown command
 |     default: // Unknown command
 | ||||||
|         error = OneWireSlaveError::INCORRECT_ONEWIRE_CMD; |         error = OneWireSlaveError::INCORRECT_ONEWIRE_CMD; | ||||||
| @ -293,8 +296,7 @@ void OneWireSlave::exti_callback(void* _pin, void* _ctx) { | |||||||
|                 if(pulse_length <= OWET::RESET_MAX) { |                 if(pulse_length <= OWET::RESET_MAX) { | ||||||
|                     // reset cycle ok
 |                     // reset cycle ok
 | ||||||
|                     bool result = _this->bus_start(); |                     bool result = _this->bus_start(); | ||||||
| 
 |                     if(result && _this->result_cb != nullptr) { | ||||||
|                     if(_this->result_cb != nullptr) { |  | ||||||
|                         _this->result_cb(result, _this->result_cb_ctx); |                         _this->result_cb(result, _this->result_cb_ctx); | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG