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 sd_filesystem(void* p); | ||||
| int32_t app_subghz(void* p); | ||||
| 
 | ||||
| int32_t gui_test(void* p); | ||||
| int32_t keypad_test(void* p); | ||||
| 
 | ||||
| @ -58,7 +57,8 @@ const FlipperApplication FLIPPER_SERVICES[] = { | ||||
|      .name = "backlight_control", | ||||
|      .stack_size = 1024, | ||||
|      .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 | ||||
| 
 | ||||
| #ifdef APP_MENU | ||||
| @ -132,7 +132,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { | ||||
| #endif | ||||
| 
 | ||||
| #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 | ||||
| 
 | ||||
| #ifdef APP_GPIO_DEMO | ||||
|  | ||||
| @ -278,6 +278,7 @@ BUILD_IBUTTON ?= 0 | ||||
| ifeq ($(BUILD_IBUTTON), 1) | ||||
| CFLAGS		+= -DBUILD_IBUTTON | ||||
| CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*.cpp) | ||||
| CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*/*.cpp) | ||||
| endif | ||||
| 
 | ||||
| APP_GUI_TEST ?= 0 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #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> | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -15,6 +16,7 @@ typedef enum { | ||||
|     GuiTesterViewDialog, | ||||
|     GuiTesterViewDialogEx, | ||||
|     GuiTesterViewPopup, | ||||
|     GuiTesterViewByteInput, | ||||
|     GuiTesterViewLast | ||||
| } GuiTesterView; | ||||
| 
 | ||||
| @ -25,13 +27,14 @@ typedef struct { | ||||
|     Submenu* submenu; | ||||
|     TextInput* text_input; | ||||
|     Popup* popup; | ||||
|     ByteInput* byte_input; | ||||
|     GuiTesterView view_index; | ||||
| } GuiTester; | ||||
| 
 | ||||
| GuiTester* gui_test_alloc(void) { | ||||
| static GuiTester* gui_test_alloc(void) { | ||||
|     GuiTester* gui_tester = furi_alloc(sizeof(GuiTester)); | ||||
|     gui_tester->view_dispatcher = view_dispatcher_alloc(); | ||||
|     gui_tester->view_index = GuiTesterViewDialogEx; | ||||
|     gui_tester->view_index = GuiTesterViewByteInput; | ||||
| 
 | ||||
|     gui_tester->dialog = dialog_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
| @ -57,10 +60,16 @@ GuiTester* gui_test_alloc(void) { | ||||
|     view_dispatcher_add_view( | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| void next_view(void* context) { | ||||
| static void next_view(void* context) { | ||||
|     furi_assert(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); | ||||
| } | ||||
| 
 | ||||
| void popup_callback(void* context) { | ||||
| static void popup_callback(void* context) { | ||||
|     next_view(context); | ||||
| } | ||||
| 
 | ||||
| void submenu_callback(void* context, uint32_t index) { | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     next_view(context); | ||||
| } | ||||
| 
 | ||||
| void dialog_callback(DialogResult result, void* context) { | ||||
| static void dialog_callback(DialogResult result, void* context) { | ||||
|     next_view(context); | ||||
| } | ||||
| 
 | ||||
| void dialog_ex_callback(DialogExResult result, void* context) { | ||||
| static void dialog_ex_callback(DialogExResult result, void* 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); | ||||
| } | ||||
| 
 | ||||
| @ -150,6 +163,34 @@ int32_t gui_test(void* param) { | ||||
|         text_input_text_len); | ||||
|     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); | ||||
| 
 | ||||
|     while(1) { | ||||
|  | ||||
| @ -135,7 +135,7 @@ void elements_multiline_text_aligned( | ||||
|     furi_assert(canvas); | ||||
|     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_init(str); | ||||
|     const char* start = text; | ||||
| @ -193,3 +193,19 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text) { | ||||
|     } while(end); | ||||
|     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); | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 | ||||
| } | ||||
| #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 | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Dialog anonymous structure */ | ||||
| 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 | ||||
|  */ | ||||
| void dialog_set_right_button_text(Dialog* dialog, const char* text); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,7 +1,10 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Dialog anonymous structure */ | ||||
| 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 | ||||
|  */ | ||||
| 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 | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Popup anonymous structure */ | ||||
| typedef struct Popup Popup; | ||||
| 
 | ||||
| @ -69,6 +72,7 @@ void popup_set_text( | ||||
|     Align vertical); | ||||
| 
 | ||||
| /* Set popup icon
 | ||||
|  * If icon position is negative, popup icon will not be rendered | ||||
|  * @param popup - Popup instance | ||||
|  * @param x, y - icon position | ||||
|  * @param name - icon to be shown | ||||
| @ -90,3 +94,7 @@ void popup_enable_timeout(Popup* popup); | ||||
|  * @param popup - Popup instance | ||||
|  */ | ||||
| void popup_disable_timeout(Popup* popup); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "text_input.h" | ||||
| #include <gui/elements.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| struct TextInput { | ||||
| @ -136,10 +137,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
| 
 | ||||
|     canvas_draw_str(canvas, 2, 8, model->header); | ||||
|     canvas_draw_line(canvas, 2, 12, canvas_width(canvas) - 7, 12); | ||||
|     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); | ||||
|     elements_slightly_rounded_frame(canvas, 1, 12, 122, 15); | ||||
| 
 | ||||
|     while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { | ||||
|         text++; | ||||
| @ -151,7 +149,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|         for(size_t column = 0; column < column_count; column++) { | ||||
|  | ||||
| @ -1,6 +1,10 @@ | ||||
| #pragma once | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Text input anonymous structure */ | ||||
| typedef struct TextInput TextInput; | ||||
| typedef void (*TextInputCallback)(void* context, char* text); | ||||
| @ -40,3 +44,7 @@ void text_input_set_result_callback( | ||||
|  * @param text - text to be shown | ||||
|  */ | ||||
| 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) { | ||||
|     furi_assert(view_dispatcher); | ||||
|     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); | ||||
| 
 | ||||
|     // 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_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(); | ||||
| } | ||||
| #include "ibutton-app.h" | ||||
| 
 | ||||
| // app enter function
 | ||||
| extern "C" int32_t app_ibutton(void* p) { | ||||
|     AppiButton* app = new AppiButton(); | ||||
|     iButtonApp* app = new iButtonApp(); | ||||
|     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) { | ||||
|         file->internal_error_id = SD_TOO_MANY_OPEN_FILES; | ||||
|     } 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
 | ||||
| @ -447,7 +447,7 @@ bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, const uint16_t name | ||||
|         } | ||||
| 
 | ||||
|         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) { | ||||
|                 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,6 +2,10 @@ | ||||
| #include "main.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_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| @ -9,3 +13,7 @@ void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| 
 | ||||
| void irda_pwm_set(float value, float freq); | ||||
| void irda_pwm_stop(); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -4,23 +4,14 @@ | ||||
| 
 | ||||
| const InputPin input_pins[] = { | ||||
|     {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true}, | ||||
|     {.port = BUTTON_DOWN_GPIO_Port, | ||||
|      .pin = BUTTON_DOWN_Pin, | ||||
|      .key = InputKeyDown, | ||||
|      .inverted = true}, | ||||
|     {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true}, | ||||
|     {.port = BUTTON_RIGHT_GPIO_Port, | ||||
|      .pin = BUTTON_RIGHT_Pin, | ||||
|      .key = InputKeyRight, | ||||
|      .inverted = true}, | ||||
|     {.port = BUTTON_LEFT_GPIO_Port, | ||||
|      .pin = BUTTON_LEFT_Pin, | ||||
|      .key = InputKeyLeft, | ||||
|      .inverted = true}, | ||||
|     {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true}, | ||||
|     {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false}, | ||||
|     {.port = BUTTON_BACK_GPIO_Port, | ||||
|      .pin = BUTTON_BACK_Pin, | ||||
|      .key = InputKeyBack, | ||||
|      .inverted = true}, | ||||
|     {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true}, | ||||
| }; | ||||
| 
 | ||||
| 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 ibutton_gpio = {iBTN_GPIO_Port, iBTN_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 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 | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -2,6 +2,10 @@ | ||||
| #include "main.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_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| @ -9,3 +13,7 @@ void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); | ||||
| 
 | ||||
| void irda_pwm_set(float value, float freq); | ||||
| void irda_pwm_stop(); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -60,6 +60,12 @@ C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | ||||
| # we build iButton application
 | ||||
| ifeq ($(BUILD_IBUTTON), 1) | ||||
| # onewire library
 | ||||
| BUILD_ONEWIRE	= 1 | ||||
| endif | ||||
| 
 | ||||
| BUILD_ONEWIRE ?= 0 | ||||
| ifeq ($(BUILD_ONEWIRE), 1) | ||||
| # onewire library
 | ||||
| ONEWIRE_DIR		= $(LIB_DIR)/onewire | ||||
| CFLAGS			+= -I$(ONEWIRE_DIR) | ||||
| CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp) | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| 
 | ||||
| OneWireMaster::OneWireMaster(const GpioPin* one_wire_gpio) { | ||||
|     gpio = one_wire_gpio; | ||||
|     reset_search(); | ||||
| } | ||||
| 
 | ||||
| OneWireMaster::~OneWireMaster() { | ||||
| @ -17,6 +18,137 @@ void OneWireMaster::stop(void) { | ||||
|     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) { | ||||
|     uint8_t r; | ||||
|     uint8_t retries = 125; | ||||
|  | ||||
| @ -7,6 +7,12 @@ class OneWireMaster { | ||||
| private: | ||||
|     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: | ||||
|     OneWireMaster(const GpioPin* one_wire_gpio); | ||||
|     ~OneWireMaster(); | ||||
| @ -19,4 +25,8 @@ public: | ||||
|     void skip(void); | ||||
|     void start(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,9 +43,11 @@ void OneWireSlave::attach(OneWireDevice* attached_device) { | ||||
| } | ||||
| 
 | ||||
| void OneWireSlave::deattach(void) { | ||||
|     device = nullptr; | ||||
|     if(device != nullptr) { | ||||
|         device->deattach(); | ||||
|     } | ||||
|     device = nullptr; | ||||
| } | ||||
| 
 | ||||
| void OneWireSlave::set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx) { | ||||
|     this->result_cb = result_cb; | ||||
| @ -237,10 +239,11 @@ bool OneWireSlave::receive_and_process_cmd(void) { | ||||
|         cmd_search_rom(); | ||||
|         return true; | ||||
| 
 | ||||
|     case 0x0F: | ||||
|     case 0x33: | ||||
|         // READ ROM
 | ||||
|         device->send_id(); | ||||
|         return false; | ||||
|         return true; | ||||
| 
 | ||||
|     default: // Unknown command
 | ||||
|         error = OneWireSlaveError::INCORRECT_ONEWIRE_CMD; | ||||
| @ -293,8 +296,7 @@ void OneWireSlave::exti_callback(void* _pin, void* _ctx) { | ||||
|                 if(pulse_length <= OWET::RESET_MAX) { | ||||
|                     // reset cycle ok
 | ||||
|                     bool result = _this->bus_start(); | ||||
| 
 | ||||
|                     if(_this->result_cb != nullptr) { | ||||
|                     if(result && _this->result_cb != nullptr) { | ||||
|                         _this->result_cb(result, _this->result_cb_ctx); | ||||
|                     } | ||||
|                 } else { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG