diff --git a/.gitignore b/.gitignore index 7ea5bafc..0d49334a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ *.swp +*.gdb_history + + +# LSP +.cache +compile_commands.json # JetBrains IDEs .idea/ diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index f2d52e66..610c5d7a 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -202,6 +202,19 @@ void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_ u8g2_DrawBox(&canvas->fb, x, y, width, height); } +void canvas_draw_rbox( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius); +} + void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { furi_assert(canvas); x += canvas->offset_x; @@ -209,6 +222,19 @@ void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint u8g2_DrawFrame(&canvas->fb, x, y, width, height); } +void canvas_draw_rframe( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius); +} + void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { furi_assert(canvas); x1 += canvas->offset_x; diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index e5674b0e..f387e20b 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -157,6 +157,28 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); */ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); +/* + * Draw rounded-corner frame of width, height at x,y, with round value raduis + */ +void canvas_draw_rframe( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius); + +/* + * Draw rounded-corner box of width, height at x,y, with round value raduis + */ +void canvas_draw_rbox( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius); + #ifdef __cplusplus } #endif diff --git a/applications/gui/elements.c b/applications/gui/elements.c index f3762418..64597f53 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -1,6 +1,8 @@ #include "elements.h" +#include "gui/canvas.h" #include #include +#include #include #include "canvas_i.h" #include @@ -44,7 +46,7 @@ void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total) { } // Position block if(total) { - uint8_t block_h = ((float)height) / total; + float block_h = ((float)height) / total; canvas_draw_box(canvas, width - 3, block_h * pos, 3, MAX(block_h, 1)); } } @@ -249,13 +251,17 @@ void elements_slightly_rounded_frame( 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); + canvas_draw_rframe(canvas, x, y, width, height, 1); +} + +void elements_slightly_rounded_box( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height) { + furi_assert(canvas); + canvas_draw_rbox(canvas, x, y, width, height, 1); } void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 76e43cff..5a13b87b 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -106,6 +106,18 @@ void elements_slightly_rounded_frame( uint8_t width, uint8_t height); +/* + * Draw slightly rounded box + * @param x, y - top left corner coordinates + * @param width, height - size of box + */ +void elements_slightly_rounded_box( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height); + /* * Trim string buffer to fit width in pixels * @param string - string to trim diff --git a/applications/gui/modules/button_menu.c b/applications/gui/modules/button_menu.c new file mode 100644 index 00000000..0545fac1 --- /dev/null +++ b/applications/gui/modules/button_menu.c @@ -0,0 +1,288 @@ +#include "button_menu.h" +#include "gui/canvas.h" +#include "gui/elements.h" +#include +#include +#include + +#define ITEM_FIRST_OFFSET 17 +#define ITEM_NEXT_OFFSET 4 +#define ITEM_HEIGHT 14 +#define ITEM_WIDTH 64 +#define BUTTONS_PER_SCREEN 6 + +struct ButtonMenuItem { + const char* label; + int32_t index; + ButtonMenuItemCallback callback; + ButtonMenuItemType type; + void* callback_context; +}; + +ARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST); + +struct ButtonMenu { + View* view; +}; + +typedef struct { + ButtonMenuItemArray_t items; + uint8_t position; + const char* header; +} ButtonMenuModel; + +static void button_menu_draw_control_button( + Canvas* canvas, + uint8_t item_position, + const char* text, + bool selected) { + furi_assert(canvas); + furi_assert(text); + + uint8_t item_x = 0; + uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); + + canvas_set_color(canvas, ColorBlack); + + if(selected) { + elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str_aligned( + canvas, + item_x + (ITEM_WIDTH / 2), + item_y + (ITEM_HEIGHT / 2), + AlignCenter, + AlignCenter, + text); +} + +static void button_menu_draw_common_button( + Canvas* canvas, + uint8_t item_position, + const char* text, + bool selected) { + furi_assert(canvas); + furi_assert(text); + + uint8_t item_x = 0; + uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); + + canvas_set_color(canvas, ColorBlack); + + if(selected) { + canvas_draw_rbox(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); + } + canvas_draw_str_aligned( + canvas, + item_x + (ITEM_WIDTH / 2), + item_y + (ITEM_HEIGHT / 2), + AlignCenter, + AlignCenter, + text); +} + +static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { + furi_assert(canvas); + furi_assert(_model); + + ButtonMenuModel* model = (ButtonMenuModel*)_model; + + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + uint8_t item_position = 0; + int8_t active_screen = model->position / BUTTONS_PER_SCREEN; + size_t items_size = ButtonMenuItemArray_size(model->items); + int8_t max_screen = ((int16_t)items_size - 1) / BUTTONS_PER_SCREEN; + ButtonMenuItemArray_it_t it; + + if(active_screen > 0) { + canvas_draw_icon_name(canvas, 28, 1, I_IrdaArrowUp_4x8); + } + + if(max_screen > active_screen) { + canvas_draw_icon_name(canvas, 28, 123, I_IrdaArrowDown_4x8); + } + + canvas_draw_str_aligned(canvas, 32, 10, AlignCenter, AlignCenter, model->header); + + for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); + ButtonMenuItemArray_next(it), ++item_position) { + if(active_screen == (item_position / BUTTONS_PER_SCREEN)) { + if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeControl) { + button_menu_draw_control_button( + canvas, + item_position % BUTTONS_PER_SCREEN, + ButtonMenuItemArray_cref(it)->label, + (item_position == model->position)); + } else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) { + button_menu_draw_common_button( + canvas, + item_position % BUTTONS_PER_SCREEN, + ButtonMenuItemArray_cref(it)->label, + (item_position == model->position)); + } + } + } +} + +static void button_menu_process_up(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + if(model->position > 0) { + model->position--; + } else { + model->position = ButtonMenuItemArray_size(model->items) - 1; + } + return true; + }); +} + +static void button_menu_process_down(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) { + model->position++; + } else { + model->position = 0; + } + return true; + }); +} + +static void button_menu_process_ok(ButtonMenu* button_menu) { + furi_assert(button_menu); + + ButtonMenuItem* item = NULL; + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + if(model->position < (ButtonMenuItemArray_size(model->items))) { + item = ButtonMenuItemArray_get(model->items, model->position); + } + return true; + }); + + if(item && item->callback) { + item->callback(item->callback_context, item->index); + } +} + +static bool button_menu_view_input_callback(InputEvent* event, void* context) { + furi_assert(event); + + ButtonMenu* button_menu = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + button_menu_process_up(button_menu); + break; + case InputKeyDown: + consumed = true; + button_menu_process_down(button_menu); + break; + case InputKeyOk: + consumed = true; + button_menu_process_ok(button_menu); + break; + default: + break; + } + } + + return consumed; +} + +View* button_menu_get_view(ButtonMenu* button_menu) { + furi_assert(button_menu); + return button_menu->view; +} + +void button_menu_clean(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + ButtonMenuItemArray_clean(model->items); + model->position = 0; + return true; + }); +} + +void button_menu_set_header(ButtonMenu* button_menu, const char* header) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + model->header = header; + return true; + }); +} + +ButtonMenuItem* button_menu_add_item( + ButtonMenu* button_menu, + const char* label, + int32_t index, + ButtonMenuItemCallback callback, + ButtonMenuItemType type, + void* callback_context) { + ButtonMenuItem* item = NULL; + furi_assert(label); + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + item = ButtonMenuItemArray_push_new(model->items); + item->label = label; + item->index = index; + item->type = type; + item->callback = callback; + item->callback_context = callback_context; + return true; + }); + + return item; +} + +ButtonMenu* button_menu_alloc(void) { + ButtonMenu* button_menu = furi_alloc(sizeof(ButtonMenu)); + button_menu->view = view_alloc(); + view_set_orientation(button_menu->view, ViewOrientationVertical); + view_set_context(button_menu->view, button_menu); + view_allocate_model(button_menu->view, ViewModelTypeLocking, sizeof(ButtonMenuModel)); + view_set_draw_callback(button_menu->view, button_menu_view_draw_callback); + view_set_input_callback(button_menu->view, button_menu_view_input_callback); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + ButtonMenuItemArray_init(model->items); + model->position = 0; + model->header = NULL; + return true; + }); + + return button_menu; +} + +void button_menu_free(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, (ButtonMenuModel * model) { + ButtonMenuItemArray_clear(model->items); + return true; + }); + view_free(button_menu->view); + free(button_menu); +} diff --git a/applications/gui/modules/button_menu.h b/applications/gui/modules/button_menu.h new file mode 100644 index 00000000..5aa25008 --- /dev/null +++ b/applications/gui/modules/button_menu.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ButtonMenu anonymous structure */ +typedef struct ButtonMenu ButtonMenu; +typedef struct ButtonMenuItem ButtonMenuItem; + +/* Callback for any button menu actions */ +typedef void (*ButtonMenuItemCallback)(void* context, int32_t index); + +/* Type of button. Difference in drawing buttons. */ +typedef enum { + ButtonMenuItemTypeCommon, + ButtonMenuItemTypeControl, +} ButtonMenuItemType; + +/** + * @brief Get button menu view + * @param button_menu - ButtonMenu instance + * @return View instance that can be used for embedding + */ +View* button_menu_get_view(ButtonMenu* button_menu); + +/** + * @brief Clean button menu + * @param button_menu - ButtonMenu instance + */ +void button_menu_clean(ButtonMenu* button_menu); + +/** + * @brief Add item to button menu instance + * @param button_menu - ButtonMenu instance + * @param label - text inside new button + * @param index - value to distinct between buttons inside ButtonMenuItemCallback + * @param type - type of button to create. Differ by button drawing. + * Control buttons have no frames, and have more squared borders. + * @return pointer to just-created item + */ +ButtonMenuItem* button_menu_add_item( + ButtonMenu* button_menu, + const char* label, + int32_t index, + ButtonMenuItemCallback callback, + ButtonMenuItemType type, + void* callback_context); + +/** + * @brief Allocate and initialize new instance of ButtonMenu model + * @return just-created ButtonMenu model + */ +ButtonMenu* button_menu_alloc(void); + +/** + * @brief Free ButtonMenu element + * @param button_menu - ButtonMenu instance + */ +void button_menu_free(ButtonMenu* button_menu); + +/** + * @brief Set ButtonMenu header on top of canvas + * @param button_menu - ButtonMenu instance + * @param header - header on the top of button menu + */ +void button_menu_set_header(ButtonMenu* button_menu, const char* header); + +#ifdef __cplusplus +} +#endif diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index 75ae96c1..d05f3302 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -1,7 +1,9 @@ #include "submenu.h" +#include "gui/canvas.h" #include #include #include +#include struct SubmenuItem { const char* label; @@ -18,6 +20,7 @@ struct Submenu { typedef struct { SubmenuItemArray_t items; + const char* header; uint8_t position; uint8_t window_position; } SubmenuModel; @@ -33,34 +36,39 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { const uint8_t item_width = 123; canvas_clear(canvas); - canvas_set_font(canvas, FontSecondary); + canvas_set_font(canvas, FontPrimary); uint8_t position = 0; SubmenuItemArray_it_t it; + if(model->header) { + canvas_draw_str(canvas, 4, 11, model->header); + } + + canvas_set_font(canvas, FontSecondary); for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); SubmenuItemArray_next(it)) { uint8_t item_position = position - model->window_position; + uint8_t elements_on_screen = model->header ? 3 : 4; + uint8_t y_offset = model->header ? 16 : 0; - if(item_position < 4) { + if(item_position < elements_on_screen) { if(position == model->position) { canvas_set_color(canvas, ColorBlack); - canvas_draw_box( - canvas, 0, (item_position * item_height) + 1, item_width, item_height - 2); + elements_slightly_rounded_box( + canvas, + 0, + y_offset + (item_position * item_height) + 1, + item_width, + item_height - 2); canvas_set_color(canvas, ColorWhite); - - canvas_draw_dot(canvas, 0, (item_position * item_height) + 1); - canvas_draw_dot(canvas, 0, (item_position * item_height) + item_height - 2); - canvas_draw_dot(canvas, item_width - 1, (item_position * item_height) + 1); - canvas_draw_dot( - canvas, item_width - 1, (item_position * item_height) + item_height - 2); } else { canvas_set_color(canvas, ColorBlack); } canvas_draw_str( canvas, 6, - (item_position * item_height) + item_height - 4, + y_offset + (item_position * item_height) + item_height - 4, SubmenuItemArray_cref(it)->label); } @@ -110,6 +118,7 @@ Submenu* submenu_alloc() { SubmenuItemArray_init(model->items); model->position = 0; model->window_position = 0; + model->header = NULL; return true; }); @@ -164,6 +173,7 @@ void submenu_clean(Submenu* submenu) { SubmenuItemArray_clean(model->items); model->position = 0; model->window_position = 0; + model->header = NULL; return true; }); } @@ -207,15 +217,17 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { void submenu_process_up(Submenu* submenu) { with_view_model( submenu->view, (SubmenuModel * model) { + uint8_t elements_on_screen = model->header ? 3 : 4; if(model->position > 0) { model->position--; - if((model->position - model->window_position) < 1 && model->window_position > 0) { + if(((model->position - model->window_position) < 1) && + model->window_position > 0) { model->window_position--; } } else { model->position = SubmenuItemArray_size(model->items) - 1; - if(model->position > 3) { - model->window_position = model->position - 3; + if(model->position > (elements_on_screen - 1)) { + model->window_position = model->position - (elements_on_screen - 1); } } return true; @@ -225,10 +237,12 @@ void submenu_process_up(Submenu* submenu) { void submenu_process_down(Submenu* submenu) { with_view_model( submenu->view, (SubmenuModel * model) { + uint8_t elements_on_screen = model->header ? 3 : 4; if(model->position < (SubmenuItemArray_size(model->items) - 1)) { model->position++; - if((model->position - model->window_position) > 2 && - model->window_position < (SubmenuItemArray_size(model->items) - 4)) { + if((model->position - model->window_position) > (elements_on_screen - 2) && + model->window_position < + (SubmenuItemArray_size(model->items) - elements_on_screen)) { model->window_position++; } } else { @@ -254,3 +268,13 @@ void submenu_process_ok(Submenu* submenu) { item->callback(item->callback_context, item->index); } } + +void submenu_set_header(Submenu* submenu, const char* header) { + furi_assert(submenu); + + with_view_model( + submenu->view, (SubmenuModel * model) { + model->header = header; + return true; + }); +} diff --git a/applications/gui/modules/submenu.h b/applications/gui/modules/submenu.h index 7350d257..64cb9b96 100644 --- a/applications/gui/modules/submenu.h +++ b/applications/gui/modules/submenu.h @@ -16,12 +16,6 @@ typedef void (*SubmenuItemCallback)(void* context, uint32_t index); */ Submenu* submenu_alloc(); -/** - * @brief Allocate and initialize submenu for vertical display - * This submenu is used to select one option - */ -Submenu* submenu_vertical_alloc(); - /** * @brief Deinitialize and free submenu * @param submenu - Submenu instance @@ -59,11 +53,18 @@ void submenu_clean(Submenu* submenu); /** * @brief Set submenu item selector - * @param submenu - * @param index + * @param submenu + * @param index */ void submenu_set_selected_item(Submenu* submenu, uint32_t index); +/** + * @brief Set optional header for submenu + * @param submenu - submenu entity + * @param header - header to set + */ +void submenu_set_header(Submenu* submenu, const char* header); + #ifdef __cplusplus } #endif diff --git a/applications/irda/irda-app-event.hpp b/applications/irda/irda-app-event.hpp new file mode 100644 index 00000000..93e323d4 --- /dev/null +++ b/applications/irda/irda-app-event.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include + +class IrdaAppEvent { +public: + enum class Type : uint8_t { + Tick, + Back, + MenuSelected, + DialogExSelected, + NextScene, + IrdaMessageReceived, + TextEditDone, + PopupTimer, + }; + + union { + int32_t menu_index; + DialogExResult dialog_ex_result; + } payload; + + Type type; +}; + diff --git a/applications/irda/irda-app-receiver.cpp b/applications/irda/irda-app-receiver.cpp new file mode 100644 index 00000000..7e792979 --- /dev/null +++ b/applications/irda/irda-app-receiver.cpp @@ -0,0 +1,46 @@ +#include "irda-app.hpp" +#include "irda.h" +#include + +void IrdaAppSignalReceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) { + IrdaAppEvent event; + const IrdaMessage* irda_message; + IrdaAppSignalReceiver* this_ = static_cast(ctx); + + irda_message = irda_decode(this_->decoder, level, duration); + if(irda_message) { + this_->capture_stop(); + this_->message = *irda_message; + event.type = IrdaAppEvent::Type::IrdaMessageReceived; + osStatus_t result = osMessageQueuePut(this_->event_queue, &event, 0, 0); + furi_check(result == osOK); + } +} + +IrdaAppSignalReceiver::IrdaAppSignalReceiver(void) + : decoder(irda_alloc_decoder()) { +} + +IrdaAppSignalReceiver::~IrdaAppSignalReceiver() { + api_hal_irda_rx_irq_deinit(); + irda_free_decoder(decoder); +} + +void IrdaAppSignalReceiver::capture_once_start(osMessageQueueId_t queue) { + event_queue = queue; + irda_reset_decoder(decoder); + api_hal_irda_rx_irq_init(); + api_hal_irda_rx_irq_set_callback(IrdaAppSignalReceiver::irda_rx_callback, this); +} + +void IrdaAppSignalReceiver::capture_stop(void) { + api_hal_irda_rx_irq_deinit(); +} + +IrdaMessage* IrdaAppSignalReceiver::get_last_message(void) { + return &message; +} + +void IrdaAppSignalReceiver::send_message(const IrdaMessage* message) { + irda_send(message, 1); +} diff --git a/applications/irda/irda-app-receiver.hpp b/applications/irda/irda-app-receiver.hpp new file mode 100644 index 00000000..fc7a3221 --- /dev/null +++ b/applications/irda/irda-app-receiver.hpp @@ -0,0 +1,19 @@ +#include +#include + +class IrdaAppSignalReceiver { +public: + IrdaAppSignalReceiver(void); + ~IrdaAppSignalReceiver(void); + void capture_once_start(osMessageQueueId_t event_queue); + void capture_stop(void); + IrdaMessage* get_last_message(void); + void send_message(const IrdaMessage* message); + +private: + osMessageQueueId_t event_queue; + static void irda_rx_callback(void* ctx, bool level, uint32_t duration); + IrdaHandler* decoder; + IrdaMessage message; +}; + diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp new file mode 100644 index 00000000..7e68b4c9 --- /dev/null +++ b/applications/irda/irda-app-remote-manager.cpp @@ -0,0 +1,130 @@ +#include "irda-app-remote-manager.hpp" +#include "furi.h" +#include +#include + +IrdaAppRemoteManager::IrdaAppRemoteManager() { + // Read from api-hal-storage, and fill remotes +} + +static const std::string default_remote_name = "remote"; + +void IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { + remotes[current_remote_index].buttons.emplace_back(button_name, message); +} + +void IrdaAppRemoteManager::add_remote_with_button( + const char* button_name, + const IrdaMessage* message) { + bool found = true; + int i = 0; + + // find first free common name for remote + do { + found = false; + ++i; + for(const auto& it : remotes) { + if(it.name == (default_remote_name + std::to_string(i))) { + found = true; + break; + } + } + } while(found); + + remotes.emplace_back(default_remote_name + std::to_string(i)); + current_remote_index = remotes.size() - 1; + add_button(button_name, message); +} + +IrdaAppRemote::IrdaAppRemote(std::string name) + : name(name) { +} + +std::vector IrdaAppRemoteManager::get_button_list(void) const { + std::vector name_vector; + auto remote = remotes[current_remote_index]; + name_vector.reserve(remote.buttons.size()); + + for(const auto& it : remote.buttons) { + name_vector.emplace_back(it.name); + } + + // copy elision + return name_vector; +} + +std::vector IrdaAppRemoteManager::get_remote_list() const { + std::vector name_vector; + name_vector.reserve(remotes.size()); + + for(const auto& it : remotes) { + name_vector.push_back(it.name); + } + + // copy elision + return name_vector; +} + +size_t IrdaAppRemoteManager::get_current_remote(void) const { + return current_remote_index; +} + +size_t IrdaAppRemoteManager::get_current_button(void) const { + return current_button_index; +} + +void IrdaAppRemote::add_button( + size_t remote_index, + const char* button_name, + const IrdaMessage* message) { + buttons.emplace_back(button_name, message); +} + +const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t button_index) const { + furi_check(remotes[current_remote_index].buttons.size() > button_index); + auto& b = remotes[current_remote_index].buttons.at(button_index); + return &b.message; +} + +void IrdaAppRemoteManager::set_current_remote(size_t index) { + furi_check(index < remotes.size()); + current_remote_index = index; +} + +void IrdaAppRemoteManager::set_current_button(size_t index) { + furi_check(current_remote_index < remotes.size()); + furi_check(index < remotes[current_remote_index].buttons.size()); + current_button_index = index; +} + +void IrdaAppRemoteManager::delete_current_remote() { + remotes.erase(remotes.begin() + current_remote_index); + current_remote_index = 0; +} + +void IrdaAppRemoteManager::delete_current_button() { + auto& buttons = remotes[current_remote_index].buttons; + buttons.erase(buttons.begin() + current_button_index); + current_button_index = 0; +} + +std::string IrdaAppRemoteManager::get_current_button_name() { + auto buttons = remotes[current_remote_index].buttons; + return buttons[current_button_index].name; +} + +std::string IrdaAppRemoteManager::get_current_remote_name() { + return remotes[current_remote_index].name; +} + +void IrdaAppRemoteManager::rename_remote(const char* str) { + remotes[current_remote_index].name = str; +} + +void IrdaAppRemoteManager::rename_button(const char* str) { + remotes[current_remote_index].buttons[current_button_index].name = str; +} + +size_t IrdaAppRemoteManager::get_current_remote_buttons_number() { + return remotes[current_remote_index].buttons.size(); +} diff --git a/applications/irda/irda-app-remote-manager.hpp b/applications/irda/irda-app-remote-manager.hpp new file mode 100644 index 00000000..1930ba5b --- /dev/null +++ b/applications/irda/irda-app-remote-manager.hpp @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include + +class IrdaAppRemoteButton { + friend class IrdaAppRemoteManager; + std::string name; + IrdaMessage message; +public: + IrdaAppRemoteButton(const char* name, const IrdaMessage* message) + : name(name), message (*message) {} + ~IrdaAppRemoteButton() {} +}; + +class IrdaAppRemote { + friend class IrdaAppRemoteManager; + std::vector buttons; + std::string name; + bool add(const IrdaMessage*); + void add_button(size_t remote_index, const char* button_name, const IrdaMessage* message); +public: + IrdaAppRemote(std::string name); +}; + +class IrdaAppRemoteManager { + size_t current_remote_index; + size_t current_button_index; + std::vector remotes; +public: + std::vector get_remote_list() const; + std::vector get_button_list() const; + void add_remote_with_button(const char* button_name, const IrdaMessage* message); + void add_button(const char* button_name, const IrdaMessage* message); + + size_t get_current_remote(void) const; + size_t get_current_button(void) const; + const IrdaMessage* get_button_data(size_t button_index) const; + void set_current_remote(size_t index); + void set_current_button(size_t index); + void rename_button(const char* str); + void rename_remote(const char* str); + std::string get_current_button_name(); + std::string get_current_remote_name(); + size_t get_current_remote_buttons_number(); + void delete_current_button(); + void delete_current_remote(); + IrdaAppRemoteManager(); + ~IrdaAppRemoteManager() {}; +}; + diff --git a/applications/irda/irda-app-view-manager.cpp b/applications/irda/irda-app-view-manager.cpp new file mode 100644 index 00000000..ce25356f --- /dev/null +++ b/applications/irda/irda-app-view-manager.cpp @@ -0,0 +1,110 @@ +#include "furi.h" +#include "gui/modules/button_menu.h" +#include "gui/modules/dialog_ex.h" +#include "gui/modules/text_input.h" +#include "irda-app.hpp" +#include + +IrdaAppViewManager::IrdaAppViewManager() { + event_queue = osMessageQueueNew(10, sizeof(IrdaAppEvent), NULL); + + view_dispatcher = view_dispatcher_alloc(); + auto callback = cbc::obtain_connector(this, &IrdaAppViewManager::previous_view_callback); + + gui = static_cast(furi_record_open("gui")); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + + button_menu = button_menu_alloc(); + submenu = submenu_alloc(); + popup = popup_alloc(); + dialog_ex = dialog_ex_alloc(); + text_input = text_input_alloc(); + + add_view(ViewType::ButtonMenu, button_menu_get_view(button_menu)); + add_view(ViewType::Submenu, submenu_get_view(submenu)); + add_view(ViewType::Popup, popup_get_view(popup)); + add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex)); + add_view(ViewType::TextInput, text_input_get_view(text_input)); + + view_set_previous_callback(button_menu_get_view(button_menu), callback); + view_set_previous_callback(submenu_get_view(submenu), callback); + view_set_previous_callback(popup_get_view(popup), callback); + view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback); + view_set_previous_callback(text_input_get_view(text_input), callback); +} + +IrdaAppViewManager::~IrdaAppViewManager() { + view_dispatcher_remove_view( + view_dispatcher, static_cast(IrdaAppViewManager::ViewType::ButtonMenu)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(IrdaAppViewManager::ViewType::TextInput)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(IrdaAppViewManager::ViewType::DialogEx)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(IrdaAppViewManager::ViewType::Submenu)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(IrdaAppViewManager::ViewType::Popup)); + + submenu_free(submenu); + popup_free(popup); + button_menu_free(button_menu); + dialog_ex_free(dialog_ex); + text_input_free(text_input); + + view_dispatcher_free(view_dispatcher); + furi_record_close("gui"); + osMessageQueueDelete(event_queue); +} + +void IrdaAppViewManager::switch_to(ViewType type) { + view_dispatcher_switch_to_view(view_dispatcher, static_cast(type)); +} + +TextInput* IrdaAppViewManager::get_text_input() { + return text_input; +} + +DialogEx* IrdaAppViewManager::get_dialog_ex() { + return dialog_ex; +} + +Submenu* IrdaAppViewManager::get_submenu() { + return submenu; +} + +Popup* IrdaAppViewManager::get_popup() { + return popup; +} + +ButtonMenu* IrdaAppViewManager::get_button_menu() { + return button_menu; +} + +osMessageQueueId_t IrdaAppViewManager::get_event_queue() { + return event_queue; +} + +void IrdaAppViewManager::receive_event(IrdaAppEvent* event) { + if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { + event->type = IrdaAppEvent::Type::Tick; + } +} + +void IrdaAppViewManager::send_event(IrdaAppEvent* event) { + osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0); + furi_check(result == osOK); +} + +uint32_t IrdaAppViewManager::previous_view_callback(void* context) { + if(event_queue != NULL) { + IrdaAppEvent event; + event.type = IrdaAppEvent::Type::Back; + send_event(&event); + } + + return VIEW_IGNORE; +} + +void IrdaAppViewManager::add_view(ViewType view_type, View* view) { + view_dispatcher_add_view(view_dispatcher, static_cast(view_type), view); +} diff --git a/applications/irda/irda-app-view-manager.hpp b/applications/irda/irda-app-view-manager.hpp new file mode 100644 index 00000000..71097e5b --- /dev/null +++ b/applications/irda/irda-app-view-manager.hpp @@ -0,0 +1,52 @@ +#pragma once +#include "gui/modules/button_menu.h" +#include "gui/modules/text_input.h" +#include +#include +#include +#include +#include +#include "irda-app.hpp" + +class IrdaAppViewManager { +public: + enum class ViewType : uint8_t { + DialogEx, + TextInput, + Submenu, + ButtonMenu, + Popup, + }; + + IrdaAppViewManager(); + ~IrdaAppViewManager(); + + void switch_to(ViewType type); + + void receive_event(IrdaAppEvent* event); + void send_event(IrdaAppEvent* event); + + DialogEx* get_dialog_ex(); + Submenu* get_submenu(); + Popup* get_popup(); + TextInput* get_text_input(); + ButtonMenu* get_button_menu(); + + osMessageQueueId_t get_event_queue(); + + uint32_t previous_view_callback(void* context); + +private: + ViewDispatcher* view_dispatcher; + Gui* gui; + TextInput* text_input; + DialogEx* dialog_ex; + Submenu* submenu; + Popup* popup; + ButtonMenu* button_menu; + + osMessageQueueId_t event_queue; + + void add_view(ViewType view_type, View* view); +}; + diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp new file mode 100644 index 00000000..a4d69403 --- /dev/null +++ b/applications/irda/irda-app.cpp @@ -0,0 +1,156 @@ +#include "irda-app.hpp" +#include +#include +#include +#include +#include +#include + +void IrdaApp::run(void) { + IrdaAppEvent event; + bool consumed; + bool exit = false; + + scenes[current_scene]->on_enter(this); + + while(!exit) { + view_manager.receive_event(&event); + + consumed = scenes[current_scene]->on_event(this, &event); + + if(!consumed) { + if(event.type == IrdaAppEvent::Type::Back) { + exit = switch_to_previous_scene(); + } + } + }; + + scenes[current_scene]->on_exit(this); +}; + +IrdaAppViewManager* IrdaApp::get_view_manager() { + return &view_manager; +} + +void IrdaApp::set_learn_new_remote(bool value) { + learn_new_remote = value; +} + +bool IrdaApp::get_learn_new_remote() { + return learn_new_remote; +} + +void IrdaApp::switch_to_next_scene(Scene next_scene) { + previous_scenes_list.push_front(current_scene); + switch_to_next_scene_without_saving(next_scene); +} + +void IrdaApp::switch_to_next_scene_without_saving(Scene next_scene) { + if(next_scene != Scene::Exit) { + scenes[current_scene]->on_exit(this); + current_scene = next_scene; + scenes[current_scene]->on_enter(this); + } +} + +void IrdaApp::search_and_switch_to_previous_scene(const std::initializer_list& scenes_list) { + Scene previous_scene = Scene::Start; + bool scene_found = false; + + while(!scene_found) { + previous_scene = get_previous_scene(); + for(Scene element : scenes_list) { + if(previous_scene == element) { + scene_found = true; + break; + } + } + } + + scenes[current_scene]->on_exit(this); + current_scene = previous_scene; + scenes[current_scene]->on_enter(this); +} + +bool IrdaApp::switch_to_previous_scene(uint8_t count) { + Scene previous_scene = Scene::Start; + + for(uint8_t i = 0; i < count; i++) previous_scene = get_previous_scene(); + + if(previous_scene == Scene::Exit) return true; + + scenes[current_scene]->on_exit(this); + current_scene = previous_scene; + scenes[current_scene]->on_enter(this); + return false; +} + +IrdaApp::Scene IrdaApp::get_previous_scene() { + Scene scene = Scene::Exit; + + if(!previous_scenes_list.empty()) { + scene = previous_scenes_list.front(); + previous_scenes_list.pop_front(); + } + + return scene; +} + +IrdaAppRemoteManager* IrdaApp::get_remote_manager() { + return &remote_manager; +} + +IrdaAppSignalReceiver* IrdaApp::get_receiver() { + return &receiver; +} + +void IrdaApp::set_text_store(uint8_t index, const char* text...) { + furi_check(index < text_store_max); + + va_list args; + va_start(args, text); + + vsnprintf(text_store[index], text_store_size, text, args); + + va_end(args); +} + +char* IrdaApp::get_text_store(uint8_t index) { + furi_check(index < text_store_max); + + return text_store[index]; +} + +uint8_t IrdaApp::get_text_store_size() { + return text_store_size; +} + +void IrdaApp::text_input_callback(void* context, char* text) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + event.type = IrdaAppEvent::Type::TextEditDone; + app->get_view_manager()->send_event(&event); +} + +void IrdaApp::popup_callback(void* context) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + event.type = IrdaAppEvent::Type::PopupTimer; + app->get_view_manager()->send_event(&event); +} + +void IrdaApp::set_edit_element(IrdaApp::EditElement value) { + element = value; +} + +IrdaApp::EditElement IrdaApp::get_edit_element(void) { + return element; +} + +void IrdaApp::set_edit_action(IrdaApp::EditAction value) { + action = value; +} + +IrdaApp::EditAction IrdaApp::get_edit_action(void) { + return action; +} diff --git a/applications/irda/irda-app.hpp b/applications/irda/irda-app.hpp new file mode 100644 index 00000000..c1e8ce62 --- /dev/null +++ b/applications/irda/irda-app.hpp @@ -0,0 +1,108 @@ +#pragma once +#include +#include +#include +#include "irda-app-event.hpp" +#include "scene/irda-app-scene.hpp" +#include "irda-app-view-manager.hpp" +#include "irda-app-remote-manager.hpp" +#include "irda-app-receiver.hpp" +#include +#include + + +class IrdaApp { +public: + enum class EditElement : uint8_t { + Button, + Remote, + }; + enum class EditAction : uint8_t { + Rename, + Delete, + }; + enum class Scene : uint8_t { + Exit, + Start, + Universal, + UniversalTV, + UniversalAudio, + UniversalAirConditioner, + Learn, + LearnSuccess, + LearnEnterName, + LearnDone, + LearnDoneAfter, + Remote, + RemoteList, + Edit, + EditKeySelect, + EditRename, + EditDelete, + EditRenameDone, + EditDeleteDone, + }; + + void run(void); + void switch_to_next_scene(Scene index); + void switch_to_next_scene_without_saving(Scene index); + bool switch_to_previous_scene(uint8_t count = 1); + Scene get_previous_scene(); + IrdaAppViewManager* get_view_manager(); + IrdaAppSignalReceiver* get_receiver(); + void set_text_store(uint8_t index, const char* text...); + char* get_text_store(uint8_t index); + uint8_t get_text_store_size(); + IrdaAppRemoteManager* get_remote_manager(); + void search_and_switch_to_previous_scene(const std::initializer_list& scenes_list); + + void set_edit_element(EditElement value); + EditElement get_edit_element(void); + + void set_edit_action(EditAction value); + EditAction get_edit_action(void); + + bool get_learn_new_remote(); + void set_learn_new_remote(bool value); + + static void text_input_callback(void* context, char* text); + static void popup_callback(void* context); + + IrdaApp() {} + ~IrdaApp() { + for (auto &it : scenes) + delete it.second; + } +private: + static const uint8_t text_store_size = 128; + static const uint8_t text_store_max = 2; + char text_store[text_store_max][text_store_size + 1]; + bool learn_new_remote; + EditElement element; + EditAction action; + + IrdaAppSignalReceiver receiver; + IrdaAppViewManager view_manager; + IrdaAppRemoteManager remote_manager; + + std::forward_list previous_scenes_list; + Scene current_scene = Scene::Start; + + std::map scenes = { + {Scene::Start, new IrdaAppSceneStart()}, + {Scene::Universal, new IrdaAppSceneUniversal()}, + {Scene::Learn, new IrdaAppSceneLearn()}, + {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, + {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, + {Scene::LearnDone, new IrdaAppSceneLearnDone()}, + {Scene::LearnDoneAfter, new IrdaAppSceneLearnDoneAfter()}, + {Scene::Remote, new IrdaAppSceneRemote()}, + {Scene::RemoteList, new IrdaAppSceneRemoteList()}, + {Scene::Edit, new IrdaAppSceneEdit()}, + {Scene::EditKeySelect, new IrdaAppSceneEditKeySelect()}, + {Scene::EditRename, new IrdaAppSceneEditRename()}, + {Scene::EditDelete, new IrdaAppSceneEditDelete()}, + {Scene::EditRenameDone, new IrdaAppSceneEditRenameDone()}, + {Scene::EditDeleteDone, new IrdaAppSceneEditDeleteDone()}, + }; +}; diff --git a/applications/irda/irda-decoder/irda-decoder-nec.c b/applications/irda/irda-decoder/irda-decoder-nec.c deleted file mode 100644 index 4cd5880f..00000000 --- a/applications/irda/irda-decoder/irda-decoder-nec.c +++ /dev/null @@ -1,146 +0,0 @@ -#include "irda-decoder-nec.h" -#include "string.h" - -const uint32_t PREAMBULA_HIGH_MIN = 9000 - 900; -const uint32_t PREAMBULA_HIGH_MAX = 9000 + 900; - -const uint32_t PREAMBULA_LOW_MIN = 4500 - 450; -const uint32_t PREAMBULA_LOW_MAX = 4500 + 450; - -const uint32_t PREAMBULA_RETRY_LOW_MIN = 2500 - 350; -const uint32_t PREAMBULA_RETRY_LOW_MAX = 2500 + 250; - -const uint32_t BIT_HIGH_MIN = 560 - 100; -const uint32_t BIT_HIGH_MAX = 560 + 100; - -const uint32_t BIT_LOW_ONE_MIN = 1690 - 200; -const uint32_t BIT_LOW_ONE_MAX = 1690 + 200; - -const uint32_t BIT_LOW_ZERO_MIN = 560 - 100; -const uint32_t BIT_LOW_ZERO_MAX = 560 + 100; - -#define SET_STATE(_state) \ - { decoder->state = _state; } - -#define TIME_FIT(_prefix) ((time > _prefix##_MIN) && (time < _prefix##_MAX)) - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -bool save_decoder_nec_data(IrDANecDecoder* decoder, IrDADecoderOutputData* out) { - bool result = false; - - if((decoder->data.simple.cmd + decoder->data.simple.cmd_inverse) == 0xFF) { - if(out->data_length < sizeof(IrDANecDataType)) { - out->flags |= IRDA_TOO_SHORT_BUFFER; - } - - memcpy(out->data, &decoder->data.data, MIN(sizeof(IrDANecDataType), out->data_length)); - result = true; - } else { - reset_decoder_nec(decoder); - } - - return result; -} - -bool process_decoder_nec( - IrDANecDecoder* decoder, - bool polarity, - uint32_t time, - IrDADecoderOutputData* out) { - bool error = true; - bool result = false; - - switch(decoder->state) { - case(WAIT_PREAMBULA_HIGH): - if(polarity) { - if(TIME_FIT(PREAMBULA_HIGH)) { - SET_STATE(WAIT_PREAMBULA_LOW); - } - } - // any values before preambula start is correct - error = false; - break; - case(WAIT_PREAMBULA_LOW): - if(!polarity) { - if(TIME_FIT(PREAMBULA_LOW)) { - // new data, reset storage - reset_decoder_nec(decoder); - SET_STATE(WAIT_BIT_HIGH); - error = false; - } else if(TIME_FIT(PREAMBULA_RETRY_LOW)) { - // wait for data repeat command - SET_STATE(WAIT_RETRY_HIGH); - error = false; - } - } - break; - case(WAIT_RETRY_HIGH): - if(polarity) { - if(TIME_FIT(BIT_HIGH)) { - SET_STATE(WAIT_PREAMBULA_HIGH); - - // repeat event - result = save_decoder_nec_data(decoder, out); - out->flags |= IRDA_REPEAT; - error = false; - } - } - break; - case(WAIT_BIT_HIGH): - if(polarity) { - if(TIME_FIT(BIT_HIGH)) { - SET_STATE(WAIT_BIT_LOW); - error = false; - } - } - break; - case(WAIT_BIT_STOP_HIGH): - if(polarity) { - if(TIME_FIT(BIT_HIGH)) { - SET_STATE(WAIT_PREAMBULA_HIGH); - - // message end event - result = save_decoder_nec_data(decoder, out); - error = false; - } - } - break; - case(WAIT_BIT_LOW): - if(!polarity) { - int8_t bit = -1; - if(TIME_FIT(BIT_LOW_ZERO)) { - SET_STATE(WAIT_BIT_HIGH); - bit = 0; - error = false; - } else if(TIME_FIT(BIT_LOW_ONE)) { - SET_STATE(WAIT_BIT_HIGH); - bit = 1; - error = false; - } - - if(bit != -1) { - decoder->data.data |= (bit << decoder->current_data_index); - decoder->current_data_index++; - - if(decoder->current_data_index > 31) { - decoder->current_data_index = 0; - SET_STATE(WAIT_BIT_STOP_HIGH); - } - } - } - break; - } - - if(error) reset_decoder_nec(decoder); - - return result; -} - -void reset_decoder_nec(IrDANecDecoder* decoder) { - decoder->state = WAIT_PREAMBULA_HIGH; - decoder->data.data = 0; - decoder->current_data_index = 0; -} \ No newline at end of file diff --git a/applications/irda/irda-decoder/irda-decoder-nec.h b/applications/irda/irda-decoder/irda-decoder-nec.h deleted file mode 100644 index 7a6a325a..00000000 --- a/applications/irda/irda-decoder/irda-decoder-nec.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include -#include -#include "irda-decoder-types.h" - -typedef enum { - WAIT_PREAMBULA_HIGH, - WAIT_PREAMBULA_LOW, - WAIT_RETRY_HIGH, - WAIT_BIT_HIGH, - WAIT_BIT_LOW, - WAIT_BIT_STOP_HIGH, -} IrDANecDecoderState; - -typedef struct { - uint8_t addr2; - uint8_t addr1; - uint8_t cmd_inverse; - uint8_t cmd; -} IrDANecData; - -typedef uint32_t IrDANecDataType; - -typedef struct { - union { - IrDANecData simple; - IrDANecDataType data; - } data; - uint8_t current_data_index; - IrDANecDecoderState state; -} IrDANecDecoder; - -bool process_decoder_nec( - IrDANecDecoder* decoder, - bool polarity, - uint32_t time, - IrDADecoderOutputData* out); - -void reset_decoder_nec(IrDANecDecoder* decoder); \ No newline at end of file diff --git a/applications/irda/irda-decoder/irda-decoder-types.h b/applications/irda/irda-decoder/irda-decoder-types.h deleted file mode 100644 index 0ea0dbb8..00000000 --- a/applications/irda/irda-decoder/irda-decoder-types.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include - -typedef enum { IRDA_UNKNOWN, IRDA_NEC, IRDA_SAMSUNG } IrDAProtocolType; -typedef enum { IRDA_REPEAT = (1 << 0), IRDA_TOO_SHORT_BUFFER = (1 << 1) } IrDAProtocolFlags; - -typedef struct { - IrDAProtocolType protocol; - uint8_t flags; - uint8_t* data; /** < ponter to output data, filled by app */ - uint32_t data_length; /** < output data length, filled by app */ -} IrDADecoderOutputData; \ No newline at end of file diff --git a/applications/irda/irda-decoder/irda-decoder.c b/applications/irda/irda-decoder/irda-decoder.c deleted file mode 100644 index 824f5055..00000000 --- a/applications/irda/irda-decoder/irda-decoder.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "irda-decoder.h" - -IrDADecoder* alloc_decoder(void) { - IrDADecoder* decoder = malloc(sizeof(IrDADecoder)); - - // init decoders - reset_decoder_nec(&decoder->nec); - - return decoder; -} - -void free_decoder(IrDADecoder* decoder) { - free(decoder); -} - -bool process_decoder( - IrDADecoder* decoder, - bool start_polarity, - uint32_t* timings, - uint32_t timings_length, - IrDADecoderOutputData* out) { - bool result = false; - - // zero result - memset(out->data, 0, out->data_length); - out->protocol = IRDA_UNKNOWN; - out->flags = 0; - - // process data - for(uint32_t timings_index = 0; timings_index < timings_length; timings_index++) { - if(process_decoder_nec(&decoder->nec, start_polarity, timings[timings_index], out)) { - out->protocol = IRDA_NEC; - result = true; - break; - } - - start_polarity = !start_polarity; - } - - return result; -} \ No newline at end of file diff --git a/applications/irda/irda-decoder/irda-decoder.h b/applications/irda/irda-decoder/irda-decoder.h deleted file mode 100644 index 78f4415c..00000000 --- a/applications/irda/irda-decoder/irda-decoder.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include "irda-decoder-nec.h" -#include "irda-decoder-types.h" - -typedef struct { - IrDANecDecoder nec; -} IrDADecoder; - -IrDADecoder* alloc_decoder(void); -void free_decoder(IrDADecoder* decoder); -bool process_decoder( - IrDADecoder* decoder, - bool start_polarity, - uint32_t* timings, - uint32_t timings_length, - IrDADecoderOutputData* out); \ No newline at end of file diff --git a/applications/irda/irda-runner.cpp b/applications/irda/irda-runner.cpp new file mode 100644 index 00000000..9a26fa13 --- /dev/null +++ b/applications/irda/irda-runner.cpp @@ -0,0 +1,9 @@ +#include "irda-app.hpp" + +extern "C" int32_t irda(void* p) { + IrdaApp* app = new IrdaApp(); + app->run(); + delete app; + + return 0; +} diff --git a/applications/irda/irda_app.c b/applications/irda/irda_app_old.c similarity index 99% rename from applications/irda/irda_app.c rename to applications/irda/irda_app_old.c index d5aa03a5..991d068e 100644 --- a/applications/irda/irda_app.c +++ b/applications/irda/irda_app_old.c @@ -290,7 +290,7 @@ void irda_rx_callback(void* ctx, bool level, uint32_t duration) { } } -int32_t irda(void* p) { +int32_t irda2(void* p) { osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL); State _state; diff --git a/applications/irda/irda_protocols.h b/applications/irda/irda_protocols.h deleted file mode 100644 index ceb96535..00000000 --- a/applications/irda/irda_protocols.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -// our tx pin is TIM2_CH4 -extern TIM_HandleTypeDef TIM_A; - -#define RC5_CARRIER_FREQUENCY 36000 -#define RC5_DUTY_CYCLE 0.33 - -#define RC6_CARRIER_FREQUENCY 36000 -#define RC6_DUTY_CYCLE 0.33 - -#define SAMSUNG_CARRIER_FREQUENCY 37900 -#define SAMSUNG_DUTY_CYCLE 0.33 - -#define NEC_CARRIER_FREQUENCY 38000 -#define NEC_DUTY_CYCLE 0.33 - -#define SIRC_CARRIER_FREQUENCY 40000 -#define SIRC_DUTY_CYCLE 0.5 \ No newline at end of file diff --git a/applications/irda/scene/irda-app-scene-edit-delete-done.cpp b/applications/irda/scene/irda-app-scene-edit-delete-done.cpp new file mode 100644 index 00000000..927ac9ea --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit-delete-done.cpp @@ -0,0 +1,35 @@ +#include "../irda-app.hpp" + +void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Popup* popup = view_manager->get_popup(); + + popup_set_icon(popup, 0, 2, I_DolphinMafia_115x62); + popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + + popup_set_callback(popup, IrdaApp::popup_callback); + popup_set_context(popup, app); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); +} + +bool IrdaAppSceneEditDeleteDone::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::PopupTimer) { + if(app->get_edit_element() == IrdaApp::EditElement::Remote) { + app->search_and_switch_to_previous_scene( + {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); + } else { + app->search_and_switch_to_previous_scene({IrdaApp::Scene::Remote}); + } + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneEditDeleteDone::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-edit-delete.cpp b/applications/irda/scene/irda-app-scene-edit-delete.cpp new file mode 100644 index 00000000..929dd2d4 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit-delete.cpp @@ -0,0 +1,81 @@ +#include "../irda-app.hpp" +#include "irda.h" +#include +#include + +static void dialog_result_callback(DialogExResult result, void* context) { + auto app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::DialogExSelected; + event.payload.dialog_ex_result = result; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + DialogEx* dialog_ex = view_manager->get_dialog_ex(); + + auto remote_manager = app->get_remote_manager(); + + if(app->get_edit_element() == IrdaApp::EditElement::Button) { + auto message = remote_manager->get_button_data(remote_manager->get_current_button()); + dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); + app->set_text_store( + 0, + "%s\n%s\nA=0x%0*lX C=0x%0*lX", + remote_manager->get_current_button_name().c_str(), + irda_get_protocol_name(message->protocol), + irda_get_protocol_address_length(message->protocol), + message->address, + irda_get_protocol_command_length(message->protocol), + message->command); + } else { + dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter); + app->set_text_store( + 0, + "%s\n with %lu buttons", + remote_manager->get_current_remote_name().c_str(), + remote_manager->get_current_remote_buttons_number()); + } + + dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, -1, -1, I_ButtonCenter_7x7); + 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, dialog_result_callback); + dialog_ex_set_context(dialog_ex, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx); +} + +bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::DialogExSelected) { + switch(event->payload.dialog_ex_result) { + case DialogExResultLeft: + app->switch_to_previous_scene(); + break; + case DialogExResultCenter: + furi_assert(0); + break; + case DialogExResultRight: + auto remote_manager = app->get_remote_manager(); + if(app->get_edit_element() == IrdaApp::EditElement::Remote) { + remote_manager->delete_current_remote(); + } else { + remote_manager->delete_current_button(); + } + + app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); + break; + } + } + + return consumed; +} + +void IrdaAppSceneEditDelete::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-edit-key-select.cpp b/applications/irda/scene/irda-app-scene-edit-key-select.cpp new file mode 100644 index 00000000..e0ead609 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit-key-select.cpp @@ -0,0 +1,54 @@ +#include "../irda-app.hpp" +#include "gui/modules/submenu.h" + +static void submenu_callback(void* context, uint32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + int i = 0; + + const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : + "Delete key:"; + submenu_set_header(submenu, header); + + auto remote_manager = app->get_remote_manager(); + buttons_names = remote_manager->get_button_list(); + for(const auto& it : buttons_names) { + submenu_add_item(submenu, it.c_str(), i++, submenu_callback, app); + } + + view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); +} + +bool IrdaAppSceneEditKeySelect::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + auto remote_manager = app->get_remote_manager(); + remote_manager->set_current_button(event->payload.menu_index); + consumed = true; + if(app->get_edit_action() == IrdaApp::EditAction::Rename) { + app->switch_to_next_scene(IrdaApp::Scene::EditRename); + } else { + app->switch_to_next_scene(IrdaApp::Scene::EditDelete); + } + } + + return consumed; +} + +void IrdaAppSceneEditKeySelect::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} diff --git a/applications/irda/scene/irda-app-scene-edit-rename-done.cpp b/applications/irda/scene/irda-app-scene-edit-rename-done.cpp new file mode 100644 index 00000000..7e95f2f0 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit-rename-done.cpp @@ -0,0 +1,31 @@ +#include "../irda-app.hpp" + +void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Popup* popup = view_manager->get_popup(); + + popup_set_icon(popup, 32, 5, I_DolphinNice_96x59); + + popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignTop); + + popup_set_callback(popup, IrdaApp::popup_callback); + popup_set_context(popup, app); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); +} + +bool IrdaAppSceneEditRenameDone::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::PopupTimer) { + app->switch_to_next_scene(IrdaApp::Scene::Remote); + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneEditRenameDone::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-edit-rename.cpp b/applications/irda/scene/irda-app-scene-edit-rename.cpp new file mode 100644 index 00000000..0ade41ca --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit-rename.cpp @@ -0,0 +1,46 @@ +#include "../irda-app.hpp" +#include + +void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + TextInput* text_input = view_manager->get_text_input(); + + auto remote_manager = app->get_remote_manager(); + if(app->get_edit_element() == IrdaApp::EditElement::Button) { + auto button_name = remote_manager->get_current_button_name(); + strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); + } else { + auto remote_name = remote_manager->get_current_remote_name(); + strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); + } + + text_input_set_header_text(text_input, "Name the key"); + text_input_set_result_callback( + text_input, + IrdaApp::text_input_callback, + app, + app->get_text_store(0), + app->get_text_store_size()); + + view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); +} + +bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::TextEditDone) { + auto remote_manager = app->get_remote_manager(); + if(app->get_edit_element() == IrdaApp::EditElement::Button) { + remote_manager->rename_button(app->get_text_store(0)); + } else { + remote_manager->rename_remote(app->get_text_store(0)); + } + app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneEditRename::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-edit.cpp b/applications/irda/scene/irda-app-scene-edit.cpp new file mode 100644 index 00000000..cd8a84fc --- /dev/null +++ b/applications/irda/scene/irda-app-scene-edit.cpp @@ -0,0 +1,74 @@ +#include "../irda-app.hpp" + +typedef enum { + SubmenuIndexAddKey, + SubmenuIndexRenameKey, + SubmenuIndexDeleteKey, + SubmenuIndexRenameRemote, + SubmenuIndexDeleteRemote, +} SubmenuIndex; + +static void submenu_callback(void* context, uint32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneEdit::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_add_item(submenu, "Add key", SubmenuIndexAddKey, submenu_callback, app); + submenu_add_item(submenu, "Rename key", SubmenuIndexRenameKey, submenu_callback, app); + submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app); + submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app); + submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); +} + +bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case SubmenuIndexAddKey: + app->switch_to_next_scene(IrdaApp::Scene::Learn); + break; + case SubmenuIndexRenameKey: + app->set_edit_action(IrdaApp::EditAction::Rename); + app->set_edit_element(IrdaApp::EditElement::Button); + app->switch_to_next_scene(IrdaApp::Scene::EditKeySelect); + break; + case SubmenuIndexDeleteKey: + app->set_edit_action(IrdaApp::EditAction::Delete); + app->set_edit_element(IrdaApp::EditElement::Button); + app->switch_to_next_scene(IrdaApp::Scene::EditKeySelect); + break; + case SubmenuIndexRenameRemote: + app->set_edit_action(IrdaApp::EditAction::Rename); + app->set_edit_element(IrdaApp::EditElement::Remote); + app->switch_to_next_scene(IrdaApp::Scene::EditRename); + break; + case SubmenuIndexDeleteRemote: + app->set_edit_action(IrdaApp::EditAction::Delete); + app->set_edit_element(IrdaApp::EditElement::Remote); + app->switch_to_next_scene(IrdaApp::Scene::EditDelete); + break; + } + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneEdit::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} diff --git a/applications/irda/scene/irda-app-scene-learn-done-after.cpp b/applications/irda/scene/irda-app-scene-learn-done-after.cpp new file mode 100644 index 00000000..75760333 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-learn-done-after.cpp @@ -0,0 +1,34 @@ +#include "../irda-app.hpp" +#include +#include +#include + +void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) { + auto view_manager = app->get_view_manager(); + auto popup = view_manager->get_popup(); + + popup_set_icon(popup, 0, 30, I_IrdaSendShort_128x34); + popup_set_text( + popup, "Get ready!\nPoint flipper at target.", 64, 16, AlignCenter, AlignCenter); + + popup_set_callback(popup, IrdaApp::popup_callback); + popup_set_context(popup, app); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); +} + +bool IrdaAppSceneLearnDoneAfter::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::PopupTimer) { + app->switch_to_next_scene(IrdaApp::Scene::Remote); + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneLearnDoneAfter::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-learn-done.cpp b/applications/irda/scene/irda-app-scene-learn-done.cpp new file mode 100644 index 00000000..9aff4f16 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-learn-done.cpp @@ -0,0 +1,42 @@ +#include "../irda-app.hpp" +#include +#include + +void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Popup* popup = view_manager->get_popup(); + + popup_set_icon(popup, 32, 5, I_DolphinNice_96x59); + + if(app->get_learn_new_remote()) { + popup_set_text(popup, "New remote\ncreated!", 5, 7, AlignLeft, AlignTop); + } else { + popup_set_text(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + } + + popup_set_callback(popup, IrdaApp::popup_callback); + popup_set_context(popup, app); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); +} + +bool IrdaAppSceneLearnDone::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::PopupTimer) { + if(app->get_learn_new_remote()) { + app->switch_to_next_scene(IrdaApp::Scene::LearnDoneAfter); + } else { + app->switch_to_next_scene(IrdaApp::Scene::Remote); + } + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneLearnDone::on_exit(IrdaApp* app) { + app->set_learn_new_remote(false); +} diff --git a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp new file mode 100644 index 00000000..49b581d1 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp @@ -0,0 +1,51 @@ +#include "../irda-app.hpp" +#include "gui/modules/text_input.h" +#include +#include +#include + +void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + TextInput* text_input = view_manager->get_text_input(); + + auto receiver = app->get_receiver(); + auto message = receiver->get_last_message(); + + app->set_text_store( + 0, + "%.4s_%0*lX", + irda_get_protocol_name(message->protocol), + irda_get_protocol_command_length(message->protocol), + message->command); + + text_input_set_header_text(text_input, "Name the key"); + text_input_set_result_callback( + text_input, + IrdaApp::text_input_callback, + app, + app->get_text_store(0), + app->get_text_store_size()); + + view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); +} + +bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::TextEditDone) { + auto remote_manager = app->get_remote_manager(); + auto receiver = app->get_receiver(); + if(app->get_learn_new_remote()) { + remote_manager->add_remote_with_button( + app->get_text_store(0), receiver->get_last_message()); + } else { + remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); + } + + app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); + } + return consumed; +} + +void IrdaAppSceneLearnEnterName::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-learn-success.cpp b/applications/irda/scene/irda-app-scene-learn-success.cpp new file mode 100644 index 00000000..12a7f9ad --- /dev/null +++ b/applications/irda/scene/irda-app-scene-learn-success.cpp @@ -0,0 +1,63 @@ +#include "../irda-app.hpp" +#include "irda.h" +#include +#include + +static void dialog_result_callback(DialogExResult result, void* context) { + auto app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::DialogExSelected; + event.payload.dialog_ex_result = result; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + DialogEx* dialog_ex = view_manager->get_dialog_ex(); + + auto receiver = app->get_receiver(); + auto message = receiver->get_last_message(); + + app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol)); + app->set_text_store( + 1, + "A: 0x%0*lX\nC: 0x%0*lX\n", + irda_get_protocol_address_length(message->protocol), + message->address, + irda_get_protocol_command_length(message->protocol), + message->command); + dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter); + dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Retry"); + dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_icon(dialog_ex, 0, 1, I_DolphinExcited_64x63); + dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); + dialog_ex_set_context(dialog_ex, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx); +} + +bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::DialogExSelected) { + switch(event->payload.dialog_ex_result) { + case DialogExResultLeft: + app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); + break; + case DialogExResultCenter: + furi_assert(0); + break; + case DialogExResultRight: + app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); + break; + } + } + + return consumed; +} + +void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-learn.cpp b/applications/irda/scene/irda-app-scene-learn.cpp new file mode 100644 index 00000000..993575bb --- /dev/null +++ b/applications/irda/scene/irda-app-scene-learn.cpp @@ -0,0 +1,31 @@ +#include "../irda-app.hpp" + +void IrdaAppSceneLearn::on_enter(IrdaApp* app) { + auto view_manager = app->get_view_manager(); + auto receiver = app->get_receiver(); + auto event_queue = view_manager->get_event_queue(); + + receiver->capture_once_start(event_queue); + + auto popup = view_manager->get_popup(); + + popup_set_icon(popup, 0, 32, I_IrdaLearnShort_128x31); + popup_set_text( + popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); + popup_set_callback(popup, NULL); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); +} + +bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { + app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); + } + + return consumed; +} + +void IrdaAppSceneLearn::on_exit(IrdaApp* app) { +} diff --git a/applications/irda/scene/irda-app-scene-remote-list.cpp b/applications/irda/scene/irda-app-scene-remote-list.cpp new file mode 100644 index 00000000..51afcb48 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-remote-list.cpp @@ -0,0 +1,59 @@ +#include "../irda-app.hpp" + +typedef enum { + SubmenuIndexPlus = -1, +} SubmenuIndex; + +static void submenu_callback(void* context, uint32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + auto remote_manager = app->get_remote_manager(); + int i = 0; + + remote_names = remote_manager->get_remote_list(); + for(auto& a : remote_names) { + submenu_add_item(submenu, a.c_str(), i++, submenu_callback, app); + } + submenu_add_item( + submenu, " +", SubmenuIndexPlus, submenu_callback, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); +} + +bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case SubmenuIndexPlus: + app->set_learn_new_remote(true); + app->switch_to_next_scene(IrdaApp::Scene::Learn); + break; + default: + auto remote_manager = app->get_remote_manager(); + remote_manager->set_current_remote(event->payload.menu_index); + app->switch_to_next_scene(IrdaApp::Scene::Remote); + consumed = true; + break; + } + } + + return consumed; +} + +void IrdaAppSceneRemoteList::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} diff --git a/applications/irda/scene/irda-app-scene-remote.cpp b/applications/irda/scene/irda-app-scene-remote.cpp new file mode 100644 index 00000000..ba65ead0 --- /dev/null +++ b/applications/irda/scene/irda-app-scene-remote.cpp @@ -0,0 +1,75 @@ +#include "../irda-app.hpp" +#include "gui/modules/button_menu.h" + +typedef enum { + ButtonIndexPlus = -2, + ButtonIndexEdit = -1, +} ButtonIndex; + +static void button_menu_callback(void* context, int32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneRemote::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + ButtonMenu* button_menu = view_manager->get_button_menu(); + auto remote_manager = app->get_remote_manager(); + int i = 0; + + buttons_names = remote_manager->get_button_list(); + + i = 0; + for(auto& name : buttons_names) { + button_menu_add_item( + button_menu, name.c_str(), i++, button_menu_callback, ButtonMenuItemTypeCommon, app); + } + + button_menu_add_item( + button_menu, "+", ButtonIndexPlus, button_menu_callback, ButtonMenuItemTypeControl, app); + button_menu_add_item( + button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); + + app->set_text_store(0, "%s", remote_manager->get_current_remote_name().c_str()); + button_menu_set_header(button_menu, app->get_text_store(0)); + view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); +} + +bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = true; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case ButtonIndexPlus: + app->switch_to_next_scene(IrdaApp::Scene::Learn); + break; + case ButtonIndexEdit: + app->switch_to_next_scene(IrdaApp::Scene::Edit); + break; + default: + auto remote_manager = app->get_remote_manager(); + auto message = remote_manager->get_button_data(event->payload.menu_index); + app->get_receiver()->send_message(message); + break; + } + } else if(event->type == IrdaAppEvent::Type::Back) { + app->search_and_switch_to_previous_scene( + {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); + } else { + consumed = false; + } + + return consumed; +} + +void IrdaAppSceneRemote::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + ButtonMenu* button_menu = view_manager->get_button_menu(); + + button_menu_clean(button_menu); +} diff --git a/applications/irda/scene/irda-app-scene-start.cpp b/applications/irda/scene/irda-app-scene-start.cpp new file mode 100644 index 00000000..7efb6ccb --- /dev/null +++ b/applications/irda/scene/irda-app-scene-start.cpp @@ -0,0 +1,59 @@ +#include "../irda-app.hpp" + +typedef enum { + SubmenuIndexUniversalLibrary, + SubmenuIndexLearnNewRemote, + SubmenuIndexSavedRemotes, +} SubmenuIndex; + +static void submenu_callback(void* context, uint32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneStart::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_add_item( + submenu, "Universal library", SubmenuIndexUniversalLibrary, submenu_callback, app); + submenu_add_item( + submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app); + submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); +} + +bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case SubmenuIndexUniversalLibrary: + app->switch_to_next_scene(IrdaApp::Scene::Universal); + break; + case SubmenuIndexLearnNewRemote: + app->set_learn_new_remote(true); + app->switch_to_next_scene(IrdaApp::Scene::Learn); + break; + case SubmenuIndexSavedRemotes: + app->switch_to_next_scene(IrdaApp::Scene::RemoteList); + break; + } + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneStart::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} diff --git a/applications/irda/scene/irda-app-scene-universal.cpp b/applications/irda/scene/irda-app-scene-universal.cpp new file mode 100644 index 00000000..2ab4ae2e --- /dev/null +++ b/applications/irda/scene/irda-app-scene-universal.cpp @@ -0,0 +1,57 @@ +#include "../irda-app.hpp" + +typedef enum { + SubmenuIndexUniversalTV, + SubmenuIndexUniversalAudio, + SubmenuIndexUniversalAirConditioner, +} SubmenuIndex; + +static void submenu_callback(void* context, uint32_t index) { + IrdaApp* app = static_cast(context); + IrdaAppEvent event; + + event.type = IrdaAppEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} + +void IrdaAppSceneUniversal::on_enter(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_add_item(submenu, "TV's", SubmenuIndexUniversalTV, submenu_callback, app); + submenu_add_item(submenu, "Audio Players", SubmenuIndexUniversalAudio, submenu_callback, app); + submenu_add_item( + submenu, "Air Conditioners", SubmenuIndexUniversalAirConditioner, submenu_callback, app); + + view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); +} + +bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { + bool consumed = false; + + if(event->type == IrdaAppEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case SubmenuIndexUniversalTV: + // app->switch_to_next_scene(IrdaApp::Scene::UniversalTV); + break; + case SubmenuIndexUniversalAudio: + // app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio); + break; + case SubmenuIndexUniversalAirConditioner: + // app->switch_to_next_scene(IrdaApp::Scene::UniversalAirConditioner); + break; + } + consumed = true; + } + + return consumed; +} + +void IrdaAppSceneUniversal::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} diff --git a/applications/irda/scene/irda-app-scene.hpp b/applications/irda/scene/irda-app-scene.hpp new file mode 100644 index 00000000..832e4bc0 --- /dev/null +++ b/applications/irda/scene/irda-app-scene.hpp @@ -0,0 +1,130 @@ +#pragma once +#include "../irda-app.hpp" +#include +#include "irda.h" +#include +#include +#include + +class IrdaApp; + +class IrdaAppScene { +public: + virtual void on_enter(IrdaApp* app) = 0; + virtual bool on_event(IrdaApp* app, IrdaAppEvent* event) = 0; + virtual void on_exit(IrdaApp* app) = 0; + virtual ~IrdaAppScene(){}; + +private: +}; + +class IrdaAppSceneStart : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneUniversal : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneLearn : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneLearnSuccess : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneLearnEnterName : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneLearnDone : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneLearnDoneAfter : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneRemote : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +private: + std::vector buttons_names; +}; + +class IrdaAppSceneRemoteList : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; + std::vector remote_names; +}; + +class IrdaAppSceneEdit : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneEditKeySelect : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +private: + std::vector buttons_names; +}; + +class IrdaAppSceneEditRename : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneEditDelete : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneEditRenameDone : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + +class IrdaAppSceneEditDeleteDone : public IrdaAppScene { +public: + void on_enter(IrdaApp* app) final; + bool on_event(IrdaApp* app, IrdaAppEvent* event) final; + void on_exit(IrdaApp* app) final; +}; + diff --git a/applications/irda_monitor/irda_monitor.c b/applications/irda_monitor/irda_monitor.c index 40efd647..3da88f03 100644 --- a/applications/irda_monitor/irda_monitor.c +++ b/applications/irda_monitor/irda_monitor.c @@ -110,9 +110,11 @@ int32_t irda_monitor_app(void* p) { snprintf( irda_monitor->display_text, sizeof(irda_monitor->display_text), - "%s\nA:0x%02lX\nC:0x%02lX\n%s\n", + "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", irda_get_protocol_name(message->protocol), + irda_get_protocol_address_length(message->protocol), message->address, + irda_get_protocol_command_length(message->protocol), message->command, message->repeat ? " R" : ""); view_port_update(view_port); @@ -124,9 +126,11 @@ int32_t irda_monitor_app(void* p) { if(message || (distance > (IRDA_TIMINGS_SIZE / 2))) { if(message) { printf( - "== %s, A:0x%02lX, C:0x%02lX%s ==\r\n", + "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", irda_get_protocol_name(message->protocol), + irda_get_protocol_address_length(message->protocol), message->address, + irda_get_protocol_command_length(message->protocol), message->command, message->repeat ? " R" : ""); } else { diff --git a/applications/tests/irda_decoder/irda_decoder_test.c b/applications/tests/irda_decoder/irda_decoder_test.c index 6305a7e7..f3ba40b7 100644 --- a/applications/tests/irda_decoder/irda_decoder_test.c +++ b/applications/tests/irda_decoder/irda_decoder_test.c @@ -2,6 +2,7 @@ #include "../minunit.h" #include "irda.h" #include "test_data/irda_decoder_nec_test_data.srcdata" +#include "test_data/irda_decoder_necext_test_data.srcdata" #include "test_data/irda_decoder_samsung_test_data.srcdata" #define RUN_DECODER(data, expected) \ @@ -52,10 +53,12 @@ MU_TEST(test_samsung32) { RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); } -MU_TEST(test_mix_nec_samsung32) { +MU_TEST(test_mix) { + RUN_DECODER(test_necext_input1, test_necext_expected1); RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); RUN_DECODER(test_nec_input1, test_nec_expected1); RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); + RUN_DECODER(test_necext_input1, test_necext_expected1); RUN_DECODER(test_nec_input2, test_nec_expected2); } @@ -75,6 +78,11 @@ MU_TEST(test_unexpected_end_in_sequence) { RUN_DECODER(test_nec_input2, test_nec_expected2); } +MU_TEST(test_necext1) { + RUN_DECODER(test_necext_input1, test_necext_expected1); + RUN_DECODER(test_necext_input1, test_necext_expected1); +} + MU_TEST_SUITE(test_irda_decoder) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); @@ -82,7 +90,8 @@ MU_TEST_SUITE(test_irda_decoder) { MU_RUN_TEST(test_nec1); MU_RUN_TEST(test_nec2); MU_RUN_TEST(test_samsung32); - MU_RUN_TEST(test_mix_nec_samsung32); + MU_RUN_TEST(test_necext1); + MU_RUN_TEST(test_mix); } int run_minunit_test_irda_decoder() { diff --git a/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata b/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata index 69c7626b..9c2977a7 100644 --- a/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata +++ b/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata @@ -11,9 +11,9 @@ const uint32_t test_nec_input1[] = { 1415838, 9080, 4436, 611, 494, 600, 505, 578, 500, 608, 501, 602, 502, 580, 498, 606, 508, 605, 500, 583, 1633, 608, 1608, 611, 1631, 578, 1638, 602, 1614, 606, 1637, 583, 1633, 607, 1609, 611, 494, 600, 505, 570, 500, 604, 501, 602, 502, 581, 497, 606, 499, 605, 499, 583, 1633, 617, 1608, 611, 1631, 579, 1638, 602}; const IrdaMessage test_nec_expected1[] = { - {IrdaProtocolNEC, 0xFF00, 0, false}, - {IrdaProtocolNEC, 0xFF00, 0, true}, - {IrdaProtocolNEC, 0xFF00, 0, false}, + {IrdaProtocolNEC, 0x00, 0, false}, + {IrdaProtocolNEC, 0x00, 0, true}, + {IrdaProtocolNEC, 0x00, 0, false}, }; const uint32_t test_nec_input2[] = { @@ -124,57 +124,57 @@ const uint32_t test_nec_input2[] = { }; const IrdaMessage test_nec_expected2[] = { - {IrdaProtocolNEC, 0xFF00, 0x02, false}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, false}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x02, true}, - {IrdaProtocolNEC, 0xFF00, 0x06, false}, - {IrdaProtocolNEC, 0xFF00, 0x06, true}, - {IrdaProtocolNEC, 0xFF00, 0x04, false}, - {IrdaProtocolNEC, 0xFF00, 0x04, true}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, true}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, true}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x09, false}, - {IrdaProtocolNEC, 0xFF00, 0x09, false}, - {IrdaProtocolNEC, 0xFF00, 0x09, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x0A, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, true}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, true}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x0A, false}, - {IrdaProtocolNEC, 0xFF00, 0x08, false}, - {IrdaProtocolNEC, 0xFF00, 0x0A, false}, - {IrdaProtocolNEC, 0xFF00, 0x0A, true}, + {IrdaProtocolNEC, 0x00, 0x02, false}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, false}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x02, true}, + {IrdaProtocolNEC, 0x00, 0x06, false}, + {IrdaProtocolNEC, 0x00, 0x06, true}, + {IrdaProtocolNEC, 0x00, 0x04, false}, + {IrdaProtocolNEC, 0x00, 0x04, true}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, true}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, true}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x09, false}, + {IrdaProtocolNEC, 0x00, 0x09, false}, + {IrdaProtocolNEC, 0x00, 0x09, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x0A, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, true}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x08, true}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x0A, false}, + {IrdaProtocolNEC, 0x00, 0x08, false}, + {IrdaProtocolNEC, 0x00, 0x0A, false}, + {IrdaProtocolNEC, 0x00, 0x0A, true}, }; diff --git a/applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata b/applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata new file mode 100644 index 00000000..73b85de5 --- /dev/null +++ b/applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata @@ -0,0 +1,223 @@ +const uint32_t test_necext_input1[] = { +1915384, 8967, 4463, 587, 527, 590, 524, 584, 1647, 590, 524, 583, 531, 586, 527, 590, 524, 583, 1646, 589, 1640, 586, 527, 590, 524, 583, 1647, 590, 1640, 587, 1644, 582, 1647, 589, 524, 583, 531, 586, 1644, 593, 521, 586, 527, 589, 1641, 586, 528, 589, 525, 592, 521, 585, 1644, 592, 522, 585, 1645, 592, 1638, 589, 524, 592, 1637, 588, 1641, 585, 1645, 592, +41082, 8965, 2220, 591, +409594, 8972, 4458, 591, 523, 584, 530, 587, 1642, 584, 529, 588, 526, 591, 522, 583, 530, 587, 1643, 584, 1646, 590, 523, 584, 530, 587, 1643, 584, 1647, 590, 1640, 586, 1643, 583, 531, 586, 527, 589, 1641, 586, 528, 589, 524, 593, 1637, 589, 524, 593, 521, 586, 529, 589, 1641, 585, 528, 589, 1640, 586, 1644, 592, 521, 585, 1645, 592, 1638, 588, 1641, 585, +41088, 8968, 2218, 582, +95791, 8971, 2214, 587, +95787, 8965, 2220, 591, +95783, 8971, 2215, 585, +95787, 8964, 2221, 590, +95783, 8971, 2215, 586, +95788, 8965, 2220, 591, +95783, 8969, 2216, 585, +95789, 8965, 2220, 590, +95782, 8970, 2215, 586, +95788, 9047, 2139, 591, +95782, 8970, 2216, 585, +95788, 8966, 2220, 591, +95782, 8972, 2214, 588, +95786, 8964, 2222, 590, +95784, 8971, 2214, 586, +95787, 8967, 2218, 583, +95791, 8964, 2222, 588, +95785, 8969, 2217, 584, +333740, 8967, 4464, 586, 528, 590, 524, 592, 1637, 589, 525, 592, 521, 586, 528, 589, 525, 593, 1637, 588, 1640, 585, 528, 589, 525, 592, 1638, 589, 1641, 586, 1644, 592, 1638, 588, 525, 592, 522, 585, 1644, 592, 522, 585, 528, 588, 1642, 585, 529, 588, 526, 591, 522, 585, 1645, 591, 522, 584, 1646, 591, 1639, 587, 526, 591, 1639, 588, 1642, 583, 1646, 590, +41082, 8963, 2223, 588, +95785, 8967, 2219, 591, +95782, 8968, 2217, 584, +246369, 8972, 4459, 591, 523, 583, 530, 587, 1643, 583, 530, 587, 527, 590, 523, 584, 530, 586, 1643, 583, 1647, 590, 524, 583, 530, 586, 1643, 583, 1646, 589, 1641, 586, 1644, 583, 531, 586, 528, 589, 1640, 585, 528, 588, 525, 592, 1638, 588, 525, 592, 522, 585, 529, 589, 1641, 584, 529, 588, 1642, 585, 1645, 591, 522, 585, 1645, 590, 1639, 587, 1642, 584, +41090, 8966, 2220, 591, +95782, 8966, 2220, 592, +95782, 8967, 2218, 583, +165604, 9017, 4413, 586, 527, 590, 524, 583, 1647, 589, 523, 582, 531, 586, 528, 589, 525, 593, 1637, 589, 1640, 585, 527, 588, 525, 592, 1638, 589, 1641, 585, 1644, 592, 1638, 588, 525, 591, 523, 585, 1645, 591, 522, 584, 529, 588, 1642, 584, 530, 587, 527, 591, 523, 584, 1646, 591, 523, 584, 1646, 591, 1640, 586, 527, 590, 1640, 586, 1643, 583, 1646, 589, +41084, 8972, 2214, 587, +95787, 8967, 2219, 581, +95792, 8971, 2215, 586, +208929, 9016, 4414, 584, 529, 588, 526, 591, 1638, 588, 525, 591, 522, 584, 529, 587, 526, 591, 1639, 587, 1642, 584, 529, 588, 526, 591, 1638, 587, 1643, 584, 1646, 591, 1639, 587, 526, 590, 523, 584, 1646, 590, 524, 583, 530, 587, 1643, 583, 530, 587, 527, 590, 524, 583, 1647, 590, 524, 583, 1647, 590, 1640, 586, 527, 589, 1640, 586, 1644, 592, 1637, 589, +41085, 8972, 2214, 587, +95787, 8964, 2221, 590, +95784, 8965, 2221, 590, +167378, 8969, 4460, 589, 525, 582, 532, 586, 1644, 592, 521, 586, 528, 589, 524, 592, 522, 585, 1645, 592, 1638, 589, 525, 592, 522, 585, 1644, 591, 1639, 588, 1642, 585, 1645, 591, 522, 585, 529, 587, 1641, 584, 530, 587, 526, 591, 1639, 588, 526, 591, 522, 584, 530, 587, 1643, 584, 530, 587, 1642, 584, 1646, 591, 523, 584, 1647, 590, 1640, 587, 1643, 583, +41090, 9017, 2169, 591, +95781, 8969, 2216, 585, +95788, 8964, 2223, 588, +192781, 8969, 4461, 589, 525, 592, 522, 586, 1644, 592, 521, 586, 528, 589, 525, 592, 522, 585, 1644, 592, 1638, 589, 524, 592, 521, 586, 1645, 591, 1638, 588, 1642, 585, 1645, 590, 522, 584, 530, 587, 1642, 584, 530, 587, 526, 591, 1639, 588, 526, 590, 524, 583, 530, 587, 1643, 584, 530, 587, 1643, 584, 1646, 590, 524, 583, 1646, 590, 1640, 587, 1643, 583, +41090, 8967, 2219, 591, +95782, 8970, 2215, 586, +95788, 8963, 2222, 589, +179978, 8967, 4464, 586, 528, 589, 524, 593, 1637, 588, 525, 592, 522, 585, 529, 589, 525, 592, 1638, 589, 1641, 585, 529, 588, 526, 591, 1638, 588, 1641, 585, 1645, 590, 1639, 587, 527, 590, 523, 584, 1646, 591, 523, 584, 530, 587, 1643, 583, 530, 586, 527, 590, 524, 583, 1646, 590, 523, 584, 1646, 589, 1640, 586, 528, 589, 1640, 586, 1644, 593, 1638, 589, +41084, 8971, 2214, 587, +95787, 8964, 2221, 589, +95785, 8966, 2219, 592, +196616, 8967, 4463, 585, 527, 590, 525, 592, 1637, 589, 525, 592, 521, 586, 528, 589, 524, 592, 1638, 588, 1641, 585, 528, 589, 525, 592, 1637, 589, 1641, 585, 1645, 591, 1638, 588, 526, 591, 522, 585, 1645, 591, 522, 584, 530, 587, 1642, 584, 529, 588, 526, 591, 523, 583, 1645, 590, 523, 584, 1646, 590, 1639, 587, 527, 590, 1639, 586, 1644, 583, 1647, 589, +41084, 8971, 2214, 587, +95787, 8964, 2222, 589, +2112164, 8969, 4462, 588, 525, 592, 522, 585, 1645, 591, 523, 584, 529, 588, 526, 591, 523, 584, 1645, 591, 1639, 587, 527, 590, 524, 583, 1646, 590, 1639, 587, 1643, 584, 1673, 563, 524, 583, 531, 586, 1643, 593, 521, 586, 528, 589, 1641, 585, 528, 589, 525, 592, 521, 585, 1644, 592, 522, 584, 1645, 591, 1639, 588, 526, 591, 1639, 588, 1642, 583, 1646, 590, +41082, 8962, 2223, 588, +95785, 8965, 2220, 591, +95783, 8968, 2217, 583, +164778, 8969, 4462, 588, 525, 591, 522, 585, 1645, 591, 522, 585, 530, 587, 527, 591, 523, 584, 1646, 591, 1639, 588, 526, 591, 523, 583, 1646, 590, 1639, 587, 1643, 584, 1672, 564, 523, 584, 531, 586, 1643, 583, 530, 587, 527, 590, 1639, 587, 527, 589, 524, 583, 531, 586, 1644, 583, 531, 586, 1643, 583, 1647, 590, 525, 582, 1647, 589, 1639, 586, 1644, 593, +41081, 8965, 2220, 590, +95784, 8968, 2217, 583, +95790, 8970, 2215, 586, +161053, 8963, 4468, 592, 521, 586, 528, 589, 1641, 585, 529, 588, 526, 591, 522, 585, 529, 588, 1642, 585, 1645, 591, 523, 584, 530, 587, 1642, 584, 1646, 591, 1639, 586, 1669, 557, 531, 586, 527, 590, 1640, 586, 527, 590, 525, 592, 1638, 589, 525, 592, 522, 585, 528, 588, 1641, 585, 528, 588, 1642, 584, 1645, 591, 523, 584, 1645, 591, 1639, 587, 1643, 583, +41090, 8964, 2221, 590, +95784, 8963, 2222, 589, +95785, 8965, 2220, 590, +139334, 8968, 4463, 587, 527, 590, 523, 584, 1646, 590, 523, 583, 531, 586, 527, 589, 524, 583, 1647, 590, 1640, 586, 527, 590, 525, 592, 1637, 589, 1641, 585, 1644, 592, 1665, 562, 524, 591, 522, 584, 1645, 591, 523, 584, 529, 588, 1642, 584, 529, 587, 527, 590, 523, 584, 1646, 590, 523, 584, 1646, 590, 1639, 587, 527, 589, 1640, 586, 1644, 592, 1637, 589, +41085, 8970, 2217, 584, +95789, 8972, 2213, 586, +95787, 8965, 2221, 590, +141444, 8969, 4461, 589, 525, 592, 522, 584, 1644, 591, 522, 585, 529, 588, 526, 591, 522, 585, 1645, 592, 1638, 587, 526, 591, 523, 584, 1646, 591, 1639, 588, 1642, 583, 1672, 564, 523, 584, 530, 587, 1643, 584, 530, 587, 527, 590, 1640, 587, 527, 590, 524, 584, 530, 587, 1643, 584, 530, 586, 1644, 583, 1647, 589, 524, 583, 1647, 590, 1640, 586, 1645, 592, +41081, 8964, 2222, 589, +95784, 8968, 2218, 583, +95790, 8971, 2214, 586, +154119, 8969, 4462, 588, 526, 591, 522, 585, 1645, 592, 522, 585, 529, 589, 526, 591, 522, 584, 1646, 591, 1639, 588, 526, 591, 523, 583, 1645, 590, 1639, 587, 1642, 584, 1671, 564, 523, 584, 529, 587, 1643, 583, 530, 587, 527, 590, 1639, 587, 526, 590, 523, 583, 530, 586, 1643, 583, 529, 586, 1643, 583, 1646, 590, 524, 583, 1648, 589, 1641, 586, 1644, 592, +41081, 8965, 2220, 590, +95784, 8969, 2216, 585, +95790, 8964, 2221, 590, +147134, 8966, 4464, 586, 528, 589, 525, 593, 1637, 589, 524, 593, 522, 585, 529, 589, 525, 592, 1638, 589, 1641, 586, 528, 589, 525, 591, 1638, 588, 1641, 585, 1645, 592, 1664, 561, 525, 592, 523, 584, 1646, 591, 523, 584, 530, 588, 1642, 585, 526, 587, 527, 590, 523, 584, 1646, 591, 523, 584, 1646, 591, 1639, 586, 526, 590, 1640, 587, 1643, 583, 1646, 590, +41083, 8963, 2223, 587, +95786, 8965, 2221, 590, +95784, 8968, 2217, 584, +158330, 8965, 4465, 585, 529, 588, 526, 590, 1639, 587, 526, 590, 523, 584, 530, 586, 526, 590, 1639, 587, 1643, 583, 530, 587, 527, 590, 1639, 587, 1643, 584, 1647, 590, 1666, 561, 527, 589, 523, 583, 1646, 590, 523, 583, 531, 586, 1643, 583, 530, 586, 527, 590, 524, 582, 1646, 590, 525, 582, 1647, 589, 1640, 586, 528, 589, 1640, 586, 1644, 592, 1638, 589, +41085, 8971, 2214, 586, +95787, 8962, 2223, 588, +95786, 8965, 2222, 589, +206063, 8962, 4467, 591, 521, 585, 529, 588, 1642, 585, 529, 588, 525, 591, 522, 584, 530, 587, 1642, 584, 1646, 591, 523, 584, 529, 588, 1642, 583, 1646, 590, 1640, 587, 1668, 558, 530, 587, 526, 589, 1639, 586, 528, 589, 524, 583, 1647, 589, 524, 593, 521, 585, 528, 589, 1641, 585, 529, 589, 1641, 585, 1645, 592, 522, 585, 1644, 591, 1639, 587, 1642, 584, +41090, 8965, 2221, 590, +95784, 8963, 2223, 588, +95785, 8964, 2222, 589, +183026, 8970, 4460, 590, 524, 583, 531, 586, 1643, 583, 530, 587, 528, 589, 525, 592, 522, 586, 1644, 592, 1637, 588, 525, 591, 522, 585, 1645, 592, 1638, 588, 1641, 585, 1672, 565, 522, 584, 530, 588, 1642, 584, 529, 588, 526, 591, 1639, 587, 527, 590, 523, 584, 530, 587, 1642, 584, 530, 587, 1642, 583, 1647, 590, 524, 583, 1647, 590, 1640, 587, 1643, 582, +41090, 8965, 2221, 590, +95783, 8970, 2216, 584, +95789, 8962, 2223, 587, +184104, 8964, 4467, 583, 530, 587, 527, 590, 1640, 587, 527, 590, 523, 582, 531, 586, 528, 589, 1640, 586, 1644, 593, 521, 586, 528, 589, 1640, 585, 1644, 592, 1638, 589, 1667, 558, 528, 589, 526, 591, 1638, 588, 526, 591, 522, 585, 1645, 591, 522, 585, 530, 587, 527, 591, 1639, 587, 526, 591, 1639, 587, 1642, 584, 530, 587, 1643, 583, 1646, 590, 1639, 587, +41087, 9020, 2166, 584, +95790, 8972, 2213, 587, +95787, 8963, 2222, 589, +169833, 8964, 4465, 583, 529, 587, 527, 590, 1639, 587, 527, 591, 523, 584, 530, 586, 527, 590, 1640, 587, 1643, 583, 531, 587, 527, 590, 1640, 586, 1644, 583, 1647, 590, 1666, 560, 527, 590, 524, 582, 1647, 589, 525, 592, 521, 586, 1644, 592, 521, 586, 528, 589, 526, 592, 1638, 588, 525, 592, 1638, 589, 1641, 585, 528, 589, 1641, 585, 1645, 591, 1638, 588, +41086, 8971, 2215, 585, +95789, 8964, 2222, 588, +95785, 8967, 2218, 583, +185701, 8971, 4460, 590, 523, 584, 530, 587, 1642, 584, 530, 587, 527, 590, 524, 583, 531, 586, 1644, 583, 1647, 590, 524, 592, 521, 585, 1644, 592, 1638, 589, 1641, 585, 1671, 565, 522, 586, 529, 588, 1642, 585, 529, 588, 526, 591, 1638, 588, 525, 590, 523, 584, 530, 587, 1642, 584, 530, 587, 1642, 584, 1646, 590, 524, 583, 1646, 590, 1640, 586, 1643, 583, +41091, 8965, 2222, 589, +95784, 8965, 2221, 589, +95784, 8968, 2217, 583, +146332, 8969, 4461, 669, 445, 591, 522, 584, 1644, 591, 523, 584, 529, 588, 526, 591, 522, 585, 1645, 591, 1638, 587, 527, 590, 524, 584, 1646, 590, 1639, 587, 1642, 583, 1673, 564, 524, 583, 531, 586, 1643, 583, 531, 586, 528, 589, 1641, 585, 528, 589, 525, 592, 522, 585, 1644, 591, 521, 585, 1645, 592, 1638, 588, 525, 592, 1638, 588, 1641, 584, 1646, 591, +41083, 8963, 2222, 589, +95785, 8966, 2220, 592, +261924, 8965, 4465, 585, 529, 588, 525, 592, 1638, 588, 525, 592, 523, 584, 530, 587, 526, 591, 1639, 587, 1642, 583, 529, 587, 527, 590, 1639, 587, 1643, 584, 1646, 590, +}; + +const IrdaMessage test_necext_expected1[] = { + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, false}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, + {IrdaProtocolNECext, 0x7984, 0x12, true}, +}; + diff --git a/assets/icons/Irda/IrdaArrowDown_4x8.png b/assets/icons/Irda/IrdaArrowDown_4x8.png new file mode 100644 index 00000000..2ac7bcdb Binary files /dev/null and b/assets/icons/Irda/IrdaArrowDown_4x8.png differ diff --git a/assets/icons/Irda/IrdaArrowUp_4x8.png b/assets/icons/Irda/IrdaArrowUp_4x8.png new file mode 100644 index 00000000..4c9a16b3 Binary files /dev/null and b/assets/icons/Irda/IrdaArrowUp_4x8.png differ diff --git a/assets/icons/Irda/IrdaLearnShort_128x31.png b/assets/icons/Irda/IrdaLearnShort_128x31.png new file mode 100644 index 00000000..783ad087 Binary files /dev/null and b/assets/icons/Irda/IrdaLearnShort_128x31.png differ diff --git a/assets/icons/Irda/IrdaLearn_128x64.png b/assets/icons/Irda/IrdaLearn_128x64.png new file mode 100644 index 00000000..80fe3ce1 Binary files /dev/null and b/assets/icons/Irda/IrdaLearn_128x64.png differ diff --git a/assets/icons/Irda/IrdaSendShort_128x34.png b/assets/icons/Irda/IrdaSendShort_128x34.png new file mode 100644 index 00000000..377f97ef Binary files /dev/null and b/assets/icons/Irda/IrdaSendShort_128x34.png differ diff --git a/assets/icons/Irda/IrdaSend_128x64.png b/assets/icons/Irda/IrdaSend_128x64.png new file mode 100644 index 00000000..33e2cecb Binary files /dev/null and b/assets/icons/Irda/IrdaSend_128x64.png differ diff --git a/core/furi/memmgr.h b/core/furi/memmgr.h index 27d2fbf3..98e7c1d8 100644 --- a/core/furi/memmgr.h +++ b/core/furi/memmgr.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "check.h" @@ -11,11 +12,6 @@ extern "C" { // define for test case "link against furi memmgr" #define FURI_MEMMGR_GUARD 1 -void* malloc(size_t size); -void free(void* ptr); -void* realloc(void* ptr, size_t size); -void* calloc(size_t count, size_t size); - size_t memmgr_get_free_heap(void); size_t memmgr_get_minimum_free_heap(void); diff --git a/lib/irda/irda.c b/lib/irda/irda.c index 550caa5c..0d9dfc4b 100644 --- a/lib/irda/irda.c +++ b/lib/irda/irda.c @@ -12,6 +12,7 @@ struct IrdaHandler { typedef struct { IrdaAlloc alloc; IrdaDecode decode; + IrdaReset reset; IrdaFree free; } IrdaDecoders; @@ -24,6 +25,8 @@ typedef struct { const char* name; IrdaDecoders decoder; IrdaEncoders encoder; + uint8_t address_length; + uint8_t command_length; } IrdaProtocolImplementation; @@ -35,9 +38,12 @@ static const IrdaProtocolImplementation irda_protocols[] = { .decoder = { .alloc = irda_decoder_samsung32_alloc, .decode = irda_decoder_samsung32_decode, + .reset = irda_decoder_samsung32_reset, .free = irda_decoder_samsung32_free}, .encoder = { - .encode = irda_encoder_samsung32_encode} + .encode = irda_encoder_samsung32_encode}, + .address_length = 2, + .command_length = 2, }, // #1 { .protocol = IrdaProtocolNEC, @@ -45,9 +51,25 @@ static const IrdaProtocolImplementation irda_protocols[] = { .decoder = { .alloc = irda_decoder_nec_alloc, .decode = irda_decoder_nec_decode, + .reset = irda_decoder_nec_reset, .free = irda_decoder_nec_free}, .encoder = { - .encode = irda_encoder_nec_encode} + .encode = irda_encoder_nec_encode}, + .address_length = 2, + .command_length = 2, + }, + // #2 + { .protocol = IrdaProtocolNECext, + .name = "NECext", + .decoder = { + .alloc = irda_decoder_necext_alloc, + .decode = irda_decoder_nec_decode, + .reset = irda_decoder_nec_reset, + .free = irda_decoder_nec_free}, + .encoder = { + .encode = irda_encoder_necext_encode}, + .address_length = 4, + .command_length = 2, }, }; @@ -93,6 +115,12 @@ void irda_free_decoder(IrdaHandler* handler) { free(handler); } +void irda_reset_decoder(IrdaHandler* handler) { + for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { + irda_protocols[i].decoder.reset(handler->ctx[i]); + } +} + void irda_send(const IrdaMessage* message, int times) { furi_assert(message); @@ -109,3 +137,11 @@ const char* irda_get_protocol_name(IrdaProtocol protocol) { return irda_protocols[protocol].name; } +uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { + return irda_protocols[protocol].address_length; +} + +uint8_t irda_get_protocol_command_length(IrdaProtocol protocol) { + return irda_protocols[protocol].command_length; +} + diff --git a/lib/irda/irda.h b/lib/irda/irda.h index 260c2a50..42411724 100644 --- a/lib/irda/irda.h +++ b/lib/irda/irda.h @@ -13,6 +13,7 @@ typedef struct IrdaHandler IrdaHandler; typedef enum { IrdaProtocolSamsung32 = 0, IrdaProtocolNEC = 1, + IrdaProtocolNECext = 2, } IrdaProtocol; typedef struct { @@ -50,6 +51,13 @@ const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t durati */ void irda_free_decoder(IrdaHandler* handler); +/** + * Reset IRDA decoder. + * + * \param[in] handler - handler to irda decoders. Should be aquired with \c irda_alloc_decoder(). + */ +void irda_reset_decoder(IrdaHandler* handler); + /** * Send message over IRDA. * @@ -66,6 +74,22 @@ void irda_send(const IrdaMessage* message, int times); */ const char* irda_get_protocol_name(IrdaProtocol protocol); +/** + * Get address length by protocol enum. + * + * \param[in] protocol - protocol identifier. + * \return length of address in nibbles. + */ +uint8_t irda_get_protocol_address_length(IrdaProtocol protocol); + +/** + * Get command length by protocol enum. + * + * \param[in] protocol - protocol identifier. + * \return length of command in nibbles. + */ +uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); + #ifdef __cplusplus } #endif diff --git a/lib/irda/irda_common_decoder.c b/lib/irda/irda_common_decoder.c index ce6e7e58..8710e974 100644 --- a/lib/irda/irda_common_decoder.c +++ b/lib/irda/irda_common_decoder.c @@ -1,3 +1,4 @@ +#include "irda_common_decoder_i.h" #include #include #include "irda_i.h" @@ -164,3 +165,12 @@ void irda_common_decoder_free(void* decoder) { free(decoder); } +void irda_common_decoder_reset(void* decoder) { + furi_assert(decoder); + IrdaCommonDecoder* common_decoder = decoder; + + common_decoder->state = IrdaCommonStateWaitPreamble; + common_decoder->timings_cnt = 0; + common_decoder->databit_cnt = 0; +} + diff --git a/lib/irda/irda_common_decoder_i.h b/lib/irda/irda_common_decoder_i.h index 6a25237a..e61f6706 100644 --- a/lib/irda/irda_common_decoder_i.h +++ b/lib/irda/irda_common_decoder_i.h @@ -68,5 +68,6 @@ static inline void shift_left_array(uint32_t *array, uint32_t len, uint32_t shif IrdaMessage* irda_common_decode(IrdaCommonDecoder *decoder, bool level, uint32_t duration); void* irda_common_decoder_alloc(const IrdaCommonProtocolSpec *protocol); void irda_common_decoder_free(void* decoder); +void irda_common_decoder_reset(void* decoder); DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder); diff --git a/lib/irda/irda_i.h b/lib/irda/irda_i.h index 9f6e9e53..392ca390 100644 --- a/lib/irda/irda_i.h +++ b/lib/irda/irda_i.h @@ -7,6 +7,7 @@ typedef void* (*IrdaAlloc) (void); typedef IrdaMessage* (*IrdaDecode) (void* ctx, bool level, uint32_t duration); +typedef void (*IrdaReset) (void*); typedef void (*IrdaFree) (void*); typedef void (*IrdaEncode)(uint32_t address, uint32_t command, bool repeat); diff --git a/lib/irda/irda_protocol_defs_i.h b/lib/irda/irda_protocol_defs_i.h index a544834d..f469aa0d 100644 --- a/lib/irda/irda_protocol_defs_i.h +++ b/lib/irda/irda_protocol_defs_i.h @@ -36,7 +36,10 @@ #define IRDA_NEC_BIT_TOLERANCE 120 // us void* irda_decoder_nec_alloc(void); +void* irda_decoder_necext_alloc(void); void irda_encoder_nec_encode(uint32_t address, uint32_t command, bool repeat); +void irda_encoder_necext_encode(uint32_t address, uint32_t command, bool repeat); +void irda_decoder_nec_reset(void* decoder); void irda_decoder_nec_free(void* decoder); IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duration); @@ -73,6 +76,7 @@ IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duratio void* irda_decoder_samsung32_alloc(void); void irda_encoder_samsung32_encode(uint32_t address, uint32_t command, bool repeat); +void irda_decoder_samsung32_reset(void* decoder); void irda_decoder_samsung32_free(void* decoder); IrdaMessage* irda_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration); diff --git a/lib/irda/nec/irda_decoder_nec.c b/lib/irda/nec/irda_decoder_nec.c index 506adadd..02ab5c42 100644 --- a/lib/irda/nec/irda_decoder_nec.c +++ b/lib/irda/nec/irda_decoder_nec.c @@ -3,9 +3,12 @@ #include #include "../irda_i.h" + static bool interpret_nec(IrdaCommonDecoder* decoder); +static bool interpret_necext(IrdaCommonDecoder* decoder); static DecodeStatus decode_repeat_nec(IrdaCommonDecoder* decoder); + static const IrdaCommonProtocolSpec protocol_nec = { { IRDA_NEC_PREAMBULE_MARK, @@ -23,15 +26,33 @@ static const IrdaCommonProtocolSpec protocol_nec = { decode_repeat_nec, }; +static const IrdaCommonProtocolSpec protocol_necext = { + { + IRDA_NEC_PREAMBULE_MARK, + IRDA_NEC_PREAMBULE_SPACE, + IRDA_NEC_BIT1_MARK, + IRDA_NEC_BIT1_SPACE, + IRDA_NEC_BIT0_MARK, + IRDA_NEC_BIT0_SPACE, + IRDA_NEC_PREAMBLE_TOLERANCE, + IRDA_NEC_BIT_TOLERANCE, + }, + 32, + irda_common_decode_pdwm, + interpret_necext, + decode_repeat_nec, +}; + static bool interpret_nec(IrdaCommonDecoder* decoder) { furi_assert(decoder); bool result = false; - uint16_t address = decoder->data[0] | (decoder->data[1] << 8); + uint8_t address = decoder->data[0]; + uint8_t address_inverse = decoder->data[1]; uint8_t command = decoder->data[2]; uint8_t command_inverse = decoder->data[3]; - if((command == (uint8_t)~command_inverse)) { + if ((command == (uint8_t) ~command_inverse) && (address == (uint8_t) ~address_inverse)) { decoder->message.command = command; decoder->message.address = address; decoder->message.repeat = false; @@ -41,6 +62,24 @@ static bool interpret_nec(IrdaCommonDecoder* decoder) { return result; } +// Some NEC's extensions allow 16 bit address +static bool interpret_necext(IrdaCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint8_t command = decoder->data[2]; + uint8_t command_inverse = decoder->data[3]; + + if(command == (uint8_t)~command_inverse) { + decoder->message.command = command; + decoder->message.address = decoder->data[0] | (decoder->data[1] << 8); + decoder->message.repeat = false; + result = true; + } + + return result; +} + // timings start from Space (delay between message and repeat) static DecodeStatus decode_repeat_nec(IrdaCommonDecoder* decoder) { furi_assert(decoder); @@ -69,6 +108,10 @@ void* irda_decoder_nec_alloc(void) { return irda_common_decoder_alloc(&protocol_nec); } +void* irda_decoder_necext_alloc(void) { + return irda_common_decoder_alloc(&protocol_necext); +} + IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duration) { return irda_common_decode(decoder, level, duration); } @@ -76,3 +119,8 @@ IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duratio void irda_decoder_nec_free(void* decoder) { irda_common_decoder_free(decoder); } + +void irda_decoder_nec_reset(void* decoder) { + irda_common_decoder_reset(decoder); +} + diff --git a/lib/irda/nec/irda_encoder_nec.c b/lib/irda/nec/irda_encoder_nec.c index 5a6ca1d9..079f784e 100644 --- a/lib/irda/nec/irda_encoder_nec.c +++ b/lib/irda/nec/irda_encoder_nec.c @@ -42,3 +42,21 @@ void irda_encoder_nec_encode(uint32_t addr, uint32_t cmd, bool repeat) { } } +// Some NEC's extensions allow 16 bit address +void irda_encoder_necext_encode(uint32_t addr, uint32_t cmd, bool repeat) { + uint16_t address = addr & 0xFFFF; + uint8_t command = cmd & 0xFF; + uint8_t command_inverse = (uint8_t) ~command; + + if (!repeat) { + irda_encode_nec_preamble(); + irda_encode_byte(&encoder_timings, (uint8_t) address); + irda_encode_byte(&encoder_timings, (uint8_t) (address >> 8)); + irda_encode_byte(&encoder_timings, command); + irda_encode_byte(&encoder_timings, command_inverse); + irda_encode_bit(&encoder_timings, 1); + } else { + irda_encode_nec_repeat(); + } +} + diff --git a/lib/irda/samsung/irda_decoder_samsung.c b/lib/irda/samsung/irda_decoder_samsung.c index cebc94c4..e2eb67ef 100644 --- a/lib/irda/samsung/irda_decoder_samsung.c +++ b/lib/irda/samsung/irda_decoder_samsung.c @@ -85,3 +85,7 @@ void irda_decoder_samsung32_free(void* decoder) { irda_common_decoder_free(decoder); } +void irda_decoder_samsung32_reset(void* decoder) { + irda_common_decoder_reset(decoder); +} +