diff --git a/applications/applications.c b/applications/applications.c index e484bc8e..a7aa6d3b 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -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 diff --git a/applications/applications.mk b/applications/applications.mk index 99855836..02fd3fc2 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -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 diff --git a/applications/gui-test/gui-test.c b/applications/gui-test/gui-test.c index e4cdfce8..b0acf819 100644 --- a/applications/gui-test/gui-test.c +++ b/applications/gui-test/gui-test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include 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) { diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 8bf6b2dd..1d055164 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -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; @@ -192,4 +192,20 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, char* text) { y += font_height; } 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); } \ No newline at end of file diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 8252f81b..9815edf1 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -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 diff --git a/applications/gui/modules/byte_input.c b/applications/gui/modules/byte_input.c new file mode 100644 index 00000000..56772794 --- /dev/null +++ b/applications/gui/modules/byte_input.c @@ -0,0 +1,765 @@ +#include "byte_input.h" +#include +#include + +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; + }); +} diff --git a/applications/gui/modules/byte_input.h b/applications/gui/modules/byte_input.h new file mode 100644 index 00000000..3f3856dc --- /dev/null +++ b/applications/gui/modules/byte_input.h @@ -0,0 +1,76 @@ +#pragma once +#include + +#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 \ No newline at end of file diff --git a/applications/gui/modules/dialog.h b/applications/gui/modules/dialog.h index a34159f1..325d4b37 100644 --- a/applications/gui/modules/dialog.h +++ b/applications/gui/modules/dialog.h @@ -1,7 +1,10 @@ #pragma once - #include +#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 \ No newline at end of file diff --git a/applications/gui/modules/dialog_ex.h b/applications/gui/modules/dialog_ex.h index 9c9dce91..2c8def54 100644 --- a/applications/gui/modules/dialog_ex.h +++ b/applications/gui/modules/dialog_ex.h @@ -1,7 +1,10 @@ #pragma once - #include +#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 \ No newline at end of file diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c new file mode 100644 index 00000000..9d2172ba --- /dev/null +++ b/applications/gui/modules/file_select.c @@ -0,0 +1,358 @@ +#include "file_select.h" +#include +#include + +#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; +} \ No newline at end of file diff --git a/applications/gui/modules/file_select.h b/applications/gui/modules/file_select.h new file mode 100644 index 00000000..224ca5ef --- /dev/null +++ b/applications/gui/modules/file_select.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +#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 \ No newline at end of file diff --git a/applications/gui/modules/popup.h b/applications/gui/modules/popup.h index b7e3ac16..500a3a1c 100644 --- a/applications/gui/modules/popup.h +++ b/applications/gui/modules/popup.h @@ -1,7 +1,10 @@ #pragma once - #include +#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 @@ -89,4 +93,8 @@ void popup_enable_timeout(Popup* popup); /* Disable popup timeout * @param popup - Popup instance */ -void popup_disable_timeout(Popup* popup); \ No newline at end of file +void popup_disable_timeout(Popup* popup); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index d08071a8..eefb2eb9 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -1,4 +1,5 @@ #include "text_input.h" +#include #include 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++) { diff --git a/applications/gui/modules/text_input.h b/applications/gui/modules/text_input.h index b829ca31..f6fb3fcd 100644 --- a/applications/gui/modules/text_input.h +++ b/applications/gui/modules/text_input.h @@ -1,6 +1,10 @@ #pragma once #include +#ifdef __cplusplus +extern "C" { +#endif + /* Text input anonymous structure */ typedef struct TextInput TextInput; typedef void (*TextInputCallback)(void* context, char* text); @@ -39,4 +43,8 @@ void text_input_set_result_callback( * @param text input - Text input instance * @param text - text to be shown */ -void text_input_set_header_text(TextInput* text_input, const char* text); \ No newline at end of file +void text_input_set_header_text(TextInput* text_input, const char* text); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c index bfbd36b0..abef28c3 100644 --- a/applications/gui/view_dispatcher.c +++ b/applications/gui/view_dispatcher.c @@ -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 diff --git a/applications/ibutton/helpers/cyfral-decoder.cpp b/applications/ibutton/helpers/cyfral-decoder.cpp new file mode 100644 index 00000000..e5bcaa6b --- /dev/null +++ b/applications/ibutton/helpers/cyfral-decoder.cpp @@ -0,0 +1,193 @@ +#include "cyfral-decoder.h" +#include + +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; +} diff --git a/applications/ibutton/helpers/cyfral-decoder.h b/applications/ibutton/helpers/cyfral-decoder.h new file mode 100644 index 00000000..efddbd7f --- /dev/null +++ b/applications/ibutton/helpers/cyfral-decoder.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include + +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 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; +}; diff --git a/applications/ibutton/helpers/key-commands.h b/applications/ibutton/helpers/key-commands.h new file mode 100644 index 00000000..39c32df0 --- /dev/null +++ b/applications/ibutton/helpers/key-commands.h @@ -0,0 +1,39 @@ +#pragma once +#include + +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; +}; \ No newline at end of file diff --git a/applications/ibutton/helpers/key-emulator.cpp b/applications/ibutton/helpers/key-emulator.cpp new file mode 100644 index 00000000..486e51f1 --- /dev/null +++ b/applications/ibutton/helpers/key-emulator.cpp @@ -0,0 +1,204 @@ +#include "key-emulator.h" +#include + +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(ctx); + + _this->anything_emulated = true; +} \ No newline at end of file diff --git a/applications/ibutton/helpers/key-emulator.h b/applications/ibutton/helpers/key-emulator.h new file mode 100644 index 00000000..f426770a --- /dev/null +++ b/applications/ibutton/helpers/key-emulator.h @@ -0,0 +1,34 @@ +#pragma once +#include "pulse-sequencer.h" +#include "../ibutton-key.h" +#include +#include +#include + +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 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); +}; diff --git a/applications/ibutton/helpers/key-info.h b/applications/ibutton/helpers/key-info.h new file mode 100644 index 00000000..f64a88e5 --- /dev/null +++ b/applications/ibutton/helpers/key-info.h @@ -0,0 +1,10 @@ +#pragma once +#include + +static const uint8_t IBUTTON_KEY_SIZE = 8; + +enum class iButtonKeyType : uint8_t { + KeyDallas, + KeyCyfral, + KeyMetakom, +}; \ No newline at end of file diff --git a/applications/ibutton/helpers/key-reader.cpp b/applications/ibutton/helpers/key-reader.cpp new file mode 100644 index 00000000..1d868b8d --- /dev/null +++ b/applications/ibutton/helpers/key-reader.cpp @@ -0,0 +1,192 @@ +#include "key-reader.h" +#include "key-commands.h" +#include +#include + +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(hcomp); + KeyReader* _this = static_cast(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(); +} diff --git a/applications/ibutton/helpers/key-reader.h b/applications/ibutton/helpers/key-reader.h new file mode 100644 index 00000000..3891d095 --- /dev/null +++ b/applications/ibutton/helpers/key-reader.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include "cyfral-decoder.h" +#pragma once +#include "metakom-decoder.h" +#include "../ibutton-key.h" +#include +#include + +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(); +}; \ No newline at end of file diff --git a/applications/ibutton/helpers/key-store.cpp b/applications/ibutton/helpers/key-store.cpp new file mode 100644 index 00000000..ea33bb37 --- /dev/null +++ b/applications/ibutton/helpers/key-store.cpp @@ -0,0 +1,69 @@ +#include "key-store.h" +#include + +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)); +} diff --git a/applications/ibutton/helpers/key-store.h b/applications/ibutton/helpers/key-store.h new file mode 100644 index 00000000..8033d4d9 --- /dev/null +++ b/applications/ibutton/helpers/key-store.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#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 store; + iButtonKey* get_key(uint8_t index); +}; \ No newline at end of file diff --git a/applications/ibutton/helpers/key-worker.cpp b/applications/ibutton/helpers/key-worker.cpp new file mode 100644 index 00000000..3d17aa81 --- /dev/null +++ b/applications/ibutton/helpers/key-worker.cpp @@ -0,0 +1,51 @@ +#include "key-worker.h" +#include +#include + +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} { +} diff --git a/applications/ibutton/helpers/key-worker.h b/applications/ibutton/helpers/key-worker.h new file mode 100644 index 00000000..4595f850 --- /dev/null +++ b/applications/ibutton/helpers/key-worker.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include "key-info.h" +#include "key-reader.h" +#include "key-emulator.h" +#include "key-writer.h" +#include "../ibutton-key.h" +#include +#include + +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; +}; \ No newline at end of file diff --git a/applications/ibutton/helpers/key-writer.cpp b/applications/ibutton/helpers/key-writer.cpp new file mode 100644 index 00000000..fd587e85 --- /dev/null +++ b/applications/ibutton/helpers/key-writer.cpp @@ -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; + } +} \ No newline at end of file diff --git a/applications/ibutton/helpers/key-writer.h b/applications/ibutton/helpers/key-writer.h new file mode 100644 index 00000000..e0506d6e --- /dev/null +++ b/applications/ibutton/helpers/key-writer.h @@ -0,0 +1,35 @@ +#pragma once +#include "../ibutton-key.h" +#include + +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); +}; diff --git a/applications/ibutton/helpers/metakom-decoder.cpp b/applications/ibutton/helpers/metakom-decoder.cpp new file mode 100644 index 00000000..e0691589 --- /dev/null +++ b/applications/ibutton/helpers/metakom-decoder.cpp @@ -0,0 +1,191 @@ +#include "metakom-decoder.h" +#include + +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; +} \ No newline at end of file diff --git a/applications/ibutton/helpers/metakom-decoder.h b/applications/ibutton/helpers/metakom-decoder.h new file mode 100644 index 00000000..4a309935 --- /dev/null +++ b/applications/ibutton/helpers/metakom-decoder.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include + +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 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); +}; diff --git a/applications/ibutton/helpers/pulse-sequencer.cpp b/applications/ibutton/helpers/pulse-sequencer.cpp new file mode 100644 index 00000000..23d98318 --- /dev/null +++ b/applications/ibutton/helpers/pulse-sequencer.cpp @@ -0,0 +1,87 @@ +#include "pulse-sequencer.h" +#include +#include +#include + +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(context); + TIM_HandleTypeDef* htim = static_cast(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; + } + } +} diff --git a/applications/ibutton/helpers/pulse-sequencer.h b/applications/ibutton/helpers/pulse-sequencer.h new file mode 100644 index 00000000..03fc5bcf --- /dev/null +++ b/applications/ibutton/helpers/pulse-sequencer.h @@ -0,0 +1,26 @@ +#pragma once +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/ibutton-app.cpp b/applications/ibutton/ibutton-app.cpp new file mode 100644 index 00000000..74758280 --- /dev/null +++ b/applications/ibutton/ibutton-app.cpp @@ -0,0 +1,238 @@ +#include "ibutton-app.h" +#include + +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 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; +} \ No newline at end of file diff --git a/applications/ibutton/ibutton-app.h b/applications/ibutton/ibutton-app.h new file mode 100644 index 00000000..6b32ea5e --- /dev/null +++ b/applications/ibutton/ibutton-app.h @@ -0,0 +1,134 @@ +#pragma once +#include +#include + +#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 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 previous_scenes_list = {Scene::SceneExit}; + Scene current_scene = Scene::SceneStart; + iButtonAppViewManager view; + + std::map 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(); +}; \ No newline at end of file diff --git a/applications/ibutton/ibutton-event.h b/applications/ibutton/ibutton-event.h new file mode 100644 index 00000000..bda97ba9 --- /dev/null +++ b/applications/ibutton/ibutton-event.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +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; +}; diff --git a/applications/ibutton/ibutton-key.cpp b/applications/ibutton/ibutton-key.cpp new file mode 100644 index 00000000..16a2aa07 --- /dev/null +++ b/applications/ibutton/ibutton-key.cpp @@ -0,0 +1,78 @@ +#include "ibutton-key.h" +#include + +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() { +} diff --git a/applications/ibutton/ibutton-key.h b/applications/ibutton/ibutton-key.h new file mode 100644 index 00000000..aa693180 --- /dev/null +++ b/applications/ibutton/ibutton-key.h @@ -0,0 +1,39 @@ +#pragma once +#include +#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; +}; \ No newline at end of file diff --git a/applications/ibutton/ibutton-view-manager.cpp b/applications/ibutton/ibutton-view-manager.cpp new file mode 100644 index 00000000..9c8e6f30 --- /dev/null +++ b/applications/ibutton/ibutton-view-manager.cpp @@ -0,0 +1,126 @@ +#include "ibutton-view-manager.h" +#include "ibutton-event.h" +#include + +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(iButtonAppViewManager::Type::iButtonAppViewDialogEx), + dialog_ex_get_view(dialog_ex)); + + submenu = submenu_alloc(); + view_dispatcher_add_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewSubmenu), + submenu_get_view(submenu)); + + text_input = text_input_alloc(); + view_dispatcher_add_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewTextInput), + text_input_get_view(text_input)); + + byte_input = byte_input_alloc(); + view_dispatcher_add_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewByteInput), + byte_input_get_view(byte_input)); + + popup = popup_alloc(); + view_dispatcher_add_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewPopup), + popup_get_view(popup)); + + gui = static_cast(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(iButtonAppViewManager::Type::iButtonAppViewDialogEx)); + view_dispatcher_remove_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewSubmenu)); + view_dispatcher_remove_view( + view_dispatcher, + static_cast(iButtonAppViewManager::Type::iButtonAppViewTextInput)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(iButtonAppViewManager::Type::iButtonAppViewPopup)); + view_dispatcher_remove_view( + view_dispatcher, + static_cast(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(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; +} \ No newline at end of file diff --git a/applications/ibutton/ibutton-view-manager.h b/applications/ibutton/ibutton-view-manager.h new file mode 100644 index 00000000..0f38a880 --- /dev/null +++ b/applications/ibutton/ibutton-view-manager.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#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); +}; \ No newline at end of file diff --git a/applications/ibutton/ibutton.cpp b/applications/ibutton/ibutton.cpp index 7d6a2c8b..3f282af1 100644 --- a/applications/ibutton/ibutton.cpp +++ b/applications/ibutton/ibutton.cpp @@ -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; } \ No newline at end of file diff --git a/applications/ibutton/ibutton.h b/applications/ibutton/ibutton.h deleted file mode 100644 index 17d344e9..00000000 --- a/applications/ibutton/ibutton.h +++ /dev/null @@ -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 -class AppiButton : public AppTemplate { -public: - const GpioPin* red_led_record; - const GpioPin* green_led_record; - - static const uint8_t modes_count = 5; - AppTemplateMode* 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); -}; \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_cyfral_emulate.h b/applications/ibutton/ibutton_mode_cyfral_emulate.h deleted file mode 100644 index e0b2cb60..00000000 --- a/applications/ibutton/ibutton_mode_cyfral_emulate.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "ibutton.h" -#include "cyfral_emulator.h" - -class AppiButtonModeCyfralEmulate : public AppTemplateMode { -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(); -} \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_cyfral_read.h b/applications/ibutton/ibutton_mode_cyfral_read.h deleted file mode 100644 index 236c8e22..00000000 --- a/applications/ibutton/ibutton_mode_cyfral_read.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -#include "ibutton.h" -#include "cyfral_reader_comp.h" - -class AppiButtonModeCyfralRead : public AppTemplateMode { -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(); -} \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_dallas_emulate.h b/applications/ibutton/ibutton_mode_dallas_emulate.h deleted file mode 100644 index d502a578..00000000 --- a/applications/ibutton/ibutton_mode_dallas_emulate.h +++ /dev/null @@ -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 - -class AppiButtonModeDallasEmulate : public AppTemplateMode { -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 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(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(); -} \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_dallas_read.h b/applications/ibutton/ibutton_mode_dallas_read.h deleted file mode 100644 index 0f330342..00000000 --- a/applications/ibutton/ibutton_mode_dallas_read.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once -#include "ibutton.h" -#include "one_wire_master.h" -#include "maxim_crc.h" - -class AppiButtonModeDallasRead : public AppTemplateMode { -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(); -} \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_dallas_write.h b/applications/ibutton/ibutton_mode_dallas_write.h deleted file mode 100644 index 135511aa..00000000 --- a/applications/ibutton/ibutton_mode_dallas_write.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include "ibutton.h" -#include "blanks_writer.h" -#include "maxim_crc.h" - -class AppiButtonModeDallasWrite : public AppTemplateMode { -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(); -} \ No newline at end of file diff --git a/applications/ibutton/ibutton_mode_template.h b/applications/ibutton/ibutton_mode_template.h deleted file mode 100644 index 677422fe..00000000 --- a/applications/ibutton/ibutton_mode_template.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// template for modes -template 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; -}; diff --git a/applications/ibutton/scene/ibutton-scene-add-type.cpp b/applications/ibutton/scene/ibutton-scene-add-type.cpp new file mode 100644 index 00000000..eb465dbf --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-add-type.cpp @@ -0,0 +1,62 @@ +#include "ibutton-scene-add-type.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeMenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-add-type.h b/applications/ibutton/scene/ibutton-scene-add-type.h new file mode 100644 index 00000000..b442ba8a --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-add-type.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-add-value.cpp b/applications/ibutton/scene/ibutton-scene-add-value.cpp new file mode 100644 index 00000000..794d86ff --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-add-value.cpp @@ -0,0 +1,50 @@ +#include "ibutton-scene-add-value.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeByteEditResult; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-add-value.h b/applications/ibutton/scene/ibutton-scene-add-value.h new file mode 100644 index 00000000..673bb609 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-add-value.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp b/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp new file mode 100644 index 00000000..c9f71d25 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp @@ -0,0 +1,93 @@ +#include "ibutton-scene-delete-confirm.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeDialogResult; + event.payload.dialog_result = result; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-delete-confirm.h b/applications/ibutton/scene/ibutton-scene-delete-confirm.h new file mode 100644 index 00000000..ba9e4980 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-delete-confirm.h @@ -0,0 +1,13 @@ +#pragma once +#include "ibutton-scene-generic.h" +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-delete-success.cpp b/applications/ibutton/scene/ibutton-scene-delete-success.cpp new file mode 100644 index 00000000..1127b8f0 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-delete-success.cpp @@ -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 + +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(context); + iButtonEvent event; + event.type = iButtonEvent::Type::EventTypeBack; + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-delete-success.h b/applications/ibutton/scene/ibutton-scene-delete-success.h new file mode 100644 index 00000000..8138c42f --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-delete-success.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-emulate.cpp b/applications/ibutton/scene/ibutton-scene-emulate.cpp new file mode 100644 index 00000000..62839b54 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-emulate.cpp @@ -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 + +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); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-emulate.h b/applications/ibutton/scene/ibutton-scene-emulate.h new file mode 100644 index 00000000..640faec1 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-emulate.h @@ -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; +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-generic.h b/applications/ibutton/scene/ibutton-scene-generic.h new file mode 100644 index 00000000..07a23ff2 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-generic.h @@ -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: +}; diff --git a/applications/ibutton/scene/ibutton-scene-info.cpp b/applications/ibutton/scene/ibutton-scene-info.cpp new file mode 100644 index 00000000..905433aa --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-info.cpp @@ -0,0 +1,82 @@ +#include "ibutton-scene-info.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeDialogResult; + event.payload.dialog_result = result; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-info.h b/applications/ibutton/scene/ibutton-scene-info.h new file mode 100644 index 00000000..0fd88892 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-info.h @@ -0,0 +1,13 @@ +#pragma once +#include "ibutton-scene-generic.h" +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-crc-error.cpp b/applications/ibutton/scene/ibutton-scene-read-crc-error.cpp new file mode 100644 index 00000000..3a4796e1 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-crc-error.cpp @@ -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 + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeDialogResult; + event.payload.dialog_result = result; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-crc-error.h b/applications/ibutton/scene/ibutton-scene-read-crc-error.h new file mode 100644 index 00000000..11a158d6 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-crc-error.h @@ -0,0 +1,13 @@ +#pragma once +#include "ibutton-scene-generic.h" +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-not-key-error.cpp b/applications/ibutton/scene/ibutton-scene-read-not-key-error.cpp new file mode 100644 index 00000000..aa175baa --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-not-key-error.cpp @@ -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 + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeDialogResult; + event.payload.dialog_result = result; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-not-key-error.h b/applications/ibutton/scene/ibutton-scene-read-not-key-error.h new file mode 100644 index 00000000..3403f38c --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-not-key-error.h @@ -0,0 +1,13 @@ +#pragma once +#include "ibutton-scene-generic.h" +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-success.cpp b/applications/ibutton/scene/ibutton-scene-read-success.cpp new file mode 100644 index 00000000..4ae0a24c --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-success.cpp @@ -0,0 +1,88 @@ +#include "ibutton-scene-read-success.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeDialogResult; + event.payload.dialog_result = result; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read-success.h b/applications/ibutton/scene/ibutton-scene-read-success.h new file mode 100644 index 00000000..9578da5b --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read-success.h @@ -0,0 +1,13 @@ +#pragma once +#include "ibutton-scene-generic.h" +#include + +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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read.cpp b/applications/ibutton/scene/ibutton-scene-read.cpp new file mode 100644 index 00000000..b6cdfe7d --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read.cpp @@ -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); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-read.h b/applications/ibutton/scene/ibutton-scene-read.h new file mode 100644 index 00000000..e3ed18e8 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-read.h @@ -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: +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp b/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp new file mode 100644 index 00000000..c6e1b921 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp @@ -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 + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeMenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-readed-key-menu.h b/applications/ibutton/scene/ibutton-scene-readed-key-menu.h new file mode 100644 index 00000000..b33d1869 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-readed-key-menu.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-save-name.cpp b/applications/ibutton/scene/ibutton-scene-save-name.cpp new file mode 100644 index 00000000..7243ba5d --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-save-name.cpp @@ -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 + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeTextEditResult; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-save-name.h b/applications/ibutton/scene/ibutton-scene-save-name.h new file mode 100644 index 00000000..811b0f65 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-save-name.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-save-success.cpp b/applications/ibutton/scene/ibutton-scene-save-success.cpp new file mode 100644 index 00000000..d9c4b534 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-save-success.cpp @@ -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 + +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(context); + iButtonEvent event; + event.type = iButtonEvent::Type::EventTypeBack; + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-save-success.h b/applications/ibutton/scene/ibutton-scene-save-success.h new file mode 100644 index 00000000..e06250c5 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-save-success.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp b/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp new file mode 100644 index 00000000..c4486198 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp @@ -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 + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeMenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-saved-key-menu.h b/applications/ibutton/scene/ibutton-scene-saved-key-menu.h new file mode 100644 index 00000000..fc86d316 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-saved-key-menu.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-saved.cpp b/applications/ibutton/scene/ibutton-scene-saved.cpp new file mode 100644 index 00000000..d73755fc --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-saved.cpp @@ -0,0 +1,62 @@ +#include "ibutton-scene-saved.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeMenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-saved.h b/applications/ibutton/scene/ibutton-scene-saved.h new file mode 100644 index 00000000..99a996cd --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-saved.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-start.cpp b/applications/ibutton/scene/ibutton-scene-start.cpp new file mode 100644 index 00000000..6944dc14 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-start.cpp @@ -0,0 +1,61 @@ +#include "ibutton-scene-start.h" +#include "../ibutton-app.h" +#include "../ibutton-view-manager.h" +#include "../ibutton-event.h" +#include + +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(context); + iButtonEvent event; + + event.type = iButtonEvent::Type::EventTypeMenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-start.h b/applications/ibutton/scene/ibutton-scene-start.h new file mode 100644 index 00000000..8652b2e0 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-start.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-write-success.cpp b/applications/ibutton/scene/ibutton-scene-write-success.cpp new file mode 100644 index 00000000..2c5e77ee --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-write-success.cpp @@ -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 + +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(context); + iButtonEvent event; + event.type = iButtonEvent::Type::EventTypeBack; + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-write-success.h b/applications/ibutton/scene/ibutton-scene-write-success.h new file mode 100644 index 00000000..e0fe4c10 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-write-success.h @@ -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); +}; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-write.cpp b/applications/ibutton/scene/ibutton-scene-write.cpp new file mode 100644 index 00000000..6c109aa1 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-write.cpp @@ -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(); +} \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-write.h b/applications/ibutton/scene/ibutton-scene-write.h new file mode 100644 index 00000000..3c638217 --- /dev/null +++ b/applications/ibutton/scene/ibutton-scene-write.h @@ -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: +}; \ No newline at end of file diff --git a/applications/sd-filesystem/sd-filesystem-api.c b/applications/sd-filesystem/sd-filesystem-api.c index 426c9b5d..c63b7c51 100644 --- a/applications/sd-filesystem/sd-filesystem-api.c +++ b/applications/sd-filesystem/sd-filesystem-api.c @@ -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); } } } diff --git a/assets/icons/Common/ButtonLeftSmall_3x5.png b/assets/icons/Common/ButtonLeftSmall_3x5.png new file mode 100644 index 00000000..51411aca Binary files /dev/null and b/assets/icons/Common/ButtonLeftSmall_3x5.png differ diff --git a/assets/icons/Common/ButtonRightSmall_3x5.png b/assets/icons/Common/ButtonRightSmall_3x5.png new file mode 100644 index 00000000..b9d5f87d Binary files /dev/null and b/assets/icons/Common/ButtonRightSmall_3x5.png differ diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png new file mode 100644 index 00000000..a299d363 Binary files /dev/null and b/assets/icons/iButton/DolphinNice_96x59.png differ diff --git a/firmware/targets/f4/api-hal/api-hal-pwm.h b/firmware/targets/f4/api-hal/api-hal-pwm.h index e712ca16..b365af39 100644 --- a/firmware/targets/f4/api-hal/api-hal-pwm.h +++ b/firmware/targets/f4/api-hal/api-hal-pwm.h @@ -2,10 +2,18 @@ #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); void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); void irda_pwm_set(float value, float freq); -void irda_pwm_stop(); \ No newline at end of file +void irda_pwm_stop(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/f4/api-hal/api-hal-resources.c b/firmware/targets/f4/api-hal/api-hal-resources.c index 369e9f4b..eb7d8068 100644 --- a/firmware/targets/f4/api-hal/api-hal-resources.c +++ b/firmware/targets/f4/api-hal/api-hal-resources.c @@ -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}; \ No newline at end of file diff --git a/firmware/targets/f4/api-hal/api-hal-resources.h b/firmware/targets/f4/api-hal/api-hal-resources.h index d7a1493a..584e3d08 100644 --- a/firmware/targets/f4/api-hal/api-hal-resources.h +++ b/firmware/targets/f4/api-hal/api-hal-resources.h @@ -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 diff --git a/firmware/targets/f5/api-hal/api-hal-pwm.h b/firmware/targets/f5/api-hal/api-hal-pwm.h index e712ca16..b365af39 100644 --- a/firmware/targets/f5/api-hal/api-hal-pwm.h +++ b/firmware/targets/f5/api-hal/api-hal-pwm.h @@ -2,10 +2,18 @@ #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); void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel); void irda_pwm_set(float value, float freq); -void irda_pwm_stop(); \ No newline at end of file +void irda_pwm_stop(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lib.mk b/lib/lib.mk index 2752a990..470447b7 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -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) diff --git a/applications/ibutton/blanks_writer.cpp b/lib/onewire/blanks_writer.cpp similarity index 100% rename from applications/ibutton/blanks_writer.cpp rename to lib/onewire/blanks_writer.cpp diff --git a/applications/ibutton/blanks_writer.h b/lib/onewire/blanks_writer.h similarity index 100% rename from applications/ibutton/blanks_writer.h rename to lib/onewire/blanks_writer.h diff --git a/lib/onewire/one_wire_master.cpp b/lib/onewire/one_wire_master.cpp index 6c236b67..b7b037f1 100644 --- a/lib/onewire/one_wire_master.cpp +++ b/lib/onewire/one_wire_master.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; diff --git a/lib/onewire/one_wire_master.h b/lib/onewire/one_wire_master.h index 1272c1b1..3bdc8eaf 100644 --- a/lib/onewire/one_wire_master.h +++ b/lib/onewire/one_wire_master.h @@ -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); }; \ No newline at end of file diff --git a/lib/onewire/one_wire_slave.cpp b/lib/onewire/one_wire_slave.cpp index 0da51318..47c60920 100644 --- a/lib/onewire/one_wire_slave.cpp +++ b/lib/onewire/one_wire_slave.cpp @@ -43,8 +43,10 @@ void OneWireSlave::attach(OneWireDevice* attached_device) { } void OneWireSlave::deattach(void) { + if(device != nullptr) { + device->deattach(); + } device = nullptr; - device->deattach(); } void OneWireSlave::set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx) { @@ -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 {