diff --git a/applications/gui/modules/button_menu.c b/applications/gui/modules/button_menu.c index f496aebe..e342162e 100644 --- a/applications/gui/modules/button_menu.c +++ b/applications/gui/modules/button_menu.c @@ -1,6 +1,7 @@ #include "button_menu.h" #include "gui/canvas.h" #include "gui/elements.h" +#include "input/input.h" #include #include #include @@ -23,6 +24,7 @@ ARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST); struct ButtonMenu { View* view; + bool freeze_input; }; typedef struct { @@ -158,7 +160,7 @@ static void button_menu_process_down(ButtonMenu* button_menu) { }); } -static void button_menu_process_ok(ButtonMenu* button_menu) { +static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { furi_assert(button_menu); ButtonMenuItem* item = NULL; @@ -168,11 +170,22 @@ static void button_menu_process_ok(ButtonMenu* button_menu) { if(model->position < (ButtonMenuItemArray_size(model->items))) { item = ButtonMenuItemArray_get(model->items, model->position); } - return true; + return false; }); - if(item && item->callback) { - item->callback(item->callback_context, item->index); + if(item->type == ButtonMenuItemTypeControl) { + if(type == InputTypeShort) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } + } + } + if(item->type == ButtonMenuItemTypeCommon) { + if((type == InputTypePress) || (type == InputTypeRelease)) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } + } } } @@ -182,7 +195,19 @@ static bool button_menu_view_input_callback(InputEvent* event, void* context) { ButtonMenu* button_menu = context; bool consumed = false; - if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + if((event->type == InputTypeRelease) || (event->type == InputTypePress)) { + consumed = true; + button_menu->freeze_input = (event->type == InputTypePress); + button_menu_process_ok(button_menu, event->type); + } else if(event->type == InputTypeShort) { + consumed = true; + button_menu_process_ok(button_menu, event->type); + } + } + + if(!button_menu->freeze_input && + ((event->type == InputTypeRepeat) || (event->type == InputTypeShort))) { switch(event->key) { case InputKeyUp: consumed = true; @@ -192,10 +217,6 @@ static bool button_menu_view_input_callback(InputEvent* event, void* context) { consumed = true; button_menu_process_down(button_menu); break; - case InputKeyOk: - consumed = true; - button_menu_process_ok(button_menu); - break; default: break; } @@ -272,6 +293,7 @@ ButtonMenu* button_menu_alloc(void) { return true; }); + button_menu->freeze_input = false; return button_menu; } diff --git a/applications/gui/modules/button_menu.h b/applications/gui/modules/button_menu.h index 8228a95f..35355d6b 100644 --- a/applications/gui/modules/button_menu.h +++ b/applications/gui/modules/button_menu.h @@ -11,7 +11,7 @@ typedef struct ButtonMenu ButtonMenu; typedef struct ButtonMenuItem ButtonMenuItem; /* Callback for any button menu actions */ -typedef void (*ButtonMenuItemCallback)(void* context, int32_t index); +typedef void (*ButtonMenuItemCallback)(void* context, int32_t index, InputType type); /* Type of button. Difference in drawing buttons. */ typedef enum { diff --git a/applications/irda/cli/irda-cli.cpp b/applications/irda/cli/irda-cli.cpp index 0a28dd9a..6cde9c95 100644 --- a/applications/irda/cli/irda-cli.cpp +++ b/applications/irda/cli/irda-cli.cpp @@ -19,7 +19,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s Cli* cli = (Cli*)context; if(irda_worker_signal_is_decoded(received_signal)) { - const IrdaMessage* message = irda_worker_get_decoded_message(received_signal); + const IrdaMessage* message = irda_worker_get_decoded_signal(received_signal); buf_cnt = sniprintf( buf, sizeof(buf), @@ -54,16 +54,15 @@ static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { } IrdaWorker* worker = irda_worker_alloc(); - irda_worker_set_context(worker, cli); - irda_worker_start(worker); - irda_worker_set_received_signal_callback(worker, signal_received_callback); + irda_worker_rx_start(worker); + irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); while(!cli_cmd_interrupt_received(cli)) { delay(50); } - irda_worker_stop(worker); + irda_worker_rx_stop(worker); irda_worker_free(worker); } diff --git a/applications/irda/irda-app-brute-force.cpp b/applications/irda/irda-app-brute-force.cpp index 8709dce6..42797d21 100644 --- a/applications/irda/irda-app-brute-force.cpp +++ b/applications/irda/irda-app-brute-force.cpp @@ -1,5 +1,5 @@ -#include "irda-app-brute-force.hpp" -#include "irda/irda-app-file-parser.hpp" +#include "irda-app-brute-force.h" +#include "irda/irda-app-file-parser.h" #include "m-string.h" #include #include @@ -47,7 +47,6 @@ void IrdaAppBruteForce::stop_bruteforce() { } } -// TODO: [FL-1418] replace with timer-chained consequence of messages. bool IrdaAppBruteForce::send_next_bruteforce(void) { furi_assert(current_record.size()); furi_assert(file_parser); diff --git a/applications/irda/irda-app-brute-force.hpp b/applications/irda/irda-app-brute-force.h similarity index 85% rename from applications/irda/irda-app-brute-force.hpp rename to applications/irda/irda-app-brute-force.h index ef9ab80d..174f74f5 100644 --- a/applications/irda/irda-app-brute-force.hpp +++ b/applications/irda/irda-app-brute-force.h @@ -1,7 +1,7 @@ #pragma once #include "furi/check.h" #include -#include "irda-app-file-parser.hpp" +#include "irda-app-file-parser.h" #include class IrdaAppBruteForce { @@ -28,7 +28,9 @@ public: bool start_bruteforce(int index, int& record_amount); void add_record(int index, const char* name); - IrdaAppBruteForce(const char* filename) : universal_db_filename (filename) {} - ~IrdaAppBruteForce() {} + IrdaAppBruteForce(const char* filename) + : universal_db_filename(filename) { + } + ~IrdaAppBruteForce() { + } }; - diff --git a/applications/irda/irda-app-event.hpp b/applications/irda/irda-app-event.h similarity index 89% rename from applications/irda/irda-app-event.hpp rename to applications/irda/irda-app-event.h index eb4848fa..752ce09f 100644 --- a/applications/irda/irda-app-event.hpp +++ b/applications/irda/irda-app-event.h @@ -9,6 +9,8 @@ public: Exit, Back, MenuSelected, + MenuSelectedPress, + MenuSelectedRelease, DialogExSelected, NextScene, IrdaMessageReceived, @@ -24,4 +26,3 @@ public: Type type; }; - diff --git a/applications/irda/irda-app-file-parser.cpp b/applications/irda/irda-app-file-parser.cpp index 11fd563e..8cd0499b 100644 --- a/applications/irda/irda-app-file-parser.cpp +++ b/applications/irda/irda-app-file-parser.cpp @@ -1,6 +1,6 @@ -#include "irda-app-file-parser.hpp" +#include "irda-app-file-parser.h" #include "furi/check.h" -#include "irda-app-remote-manager.hpp" +#include "irda-app-remote-manager.h" #include "irda-app-signal.h" #include "m-string.h" #include diff --git a/applications/irda/irda-app-file-parser.hpp b/applications/irda/irda-app-file-parser.h similarity index 81% rename from applications/irda/irda-app-file-parser.hpp rename to applications/irda/irda-app-file-parser.h index 2ece1a30..f3d42e49 100644 --- a/applications/irda/irda-app-file-parser.hpp +++ b/applications/irda/irda-app-file-parser.h @@ -26,8 +26,16 @@ public: std::string make_name(const std::string& full_name) const; private: - size_t stringify_message(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); - size_t stringify_raw_signal(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); + size_t stringify_message( + const IrdaAppSignal& signal, + const char* name, + char* content, + size_t content_len); + size_t stringify_raw_signal( + const IrdaAppSignal& signal, + const char* name, + char* content, + size_t content_len); std::unique_ptr parse_signal(const std::string& str) const; std::unique_ptr parse_signal_raw(const std::string& str) const; std::string make_full_name(const std::string& name) const; @@ -41,4 +49,3 @@ private: char file_buf[128]; size_t file_buf_cnt = 0; }; - diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp index 4c339e85..cd98893e 100644 --- a/applications/irda/irda-app-remote-manager.cpp +++ b/applications/irda/irda-app-remote-manager.cpp @@ -1,4 +1,4 @@ -#include "irda-app-remote-manager.hpp" +#include "irda-app-remote-manager.h" #include #include "furi.h" #include "furi/check.h" @@ -8,7 +8,7 @@ #include #include #include -#include "irda-app-file-parser.hpp" +#include "irda-app-file-parser.h" static const std::string default_remote_name = "remote"; diff --git a/applications/irda/irda-app-remote-manager.hpp b/applications/irda/irda-app-remote-manager.h similarity index 88% rename from applications/irda/irda-app-remote-manager.hpp rename to applications/irda/irda-app-remote-manager.h index e8e0a48c..60993b30 100644 --- a/applications/irda/irda-app-remote-manager.hpp +++ b/applications/irda/irda-app-remote-manager.h @@ -12,21 +12,27 @@ class IrdaAppRemoteButton { friend class IrdaAppRemoteManager; std::string name; IrdaAppSignal signal; + public: IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal) - : name(name), signal (signal) {} - ~IrdaAppRemoteButton() {} + : name(name) + , signal(signal) { + } + ~IrdaAppRemoteButton() { + } }; class IrdaAppRemote { friend class IrdaAppRemoteManager; std::vector buttons; std::string name; -public: - IrdaAppRemote(const std::string& name) : name(name) {} - IrdaAppRemote& operator=(std::string& new_name) noexcept - { +public: + IrdaAppRemote(const std::string& name) + : name(name) { + } + + IrdaAppRemote& operator=(std::string& new_name) noexcept { name = new_name; buttons.clear(); return *this; @@ -61,4 +67,3 @@ public: bool store(); bool load(const std::string& name); }; - diff --git a/applications/irda/irda-app-view-manager.cpp b/applications/irda/irda-app-view-manager.cpp index cc2371f7..6c65b005 100644 --- a/applications/irda/irda-app-view-manager.cpp +++ b/applications/irda/irda-app-view-manager.cpp @@ -1,7 +1,7 @@ #include "furi.h" #include "gui/modules/button_panel.h" -#include "irda-app.hpp" -#include "irda/irda-app-event.hpp" +#include "irda-app.h" +#include "irda/irda-app-event.h" #include IrdaAppViewManager::IrdaAppViewManager() { @@ -112,8 +112,14 @@ void IrdaAppViewManager::receive_event(IrdaAppEvent* event) { } void IrdaAppViewManager::send_event(IrdaAppEvent* event) { - osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0); - furi_check(result == osOK); + uint32_t timeout = 0; + /* Rapid button hammering on Remote Scene causes queue overflow - ignore it, + * but try to keep button release event - it switches off IRDA DMA sending. */ + if(event->type == IrdaAppEvent::Type::MenuSelectedRelease) { + timeout = 200; + } + osMessageQueuePut(event_queue, event, 0, timeout); + /* furi_check(result == osOK); */ } uint32_t IrdaAppViewManager::previous_view_callback(void* context) { diff --git a/applications/irda/irda-app-view-manager.hpp b/applications/irda/irda-app-view-manager.h similarity index 98% rename from applications/irda/irda-app-view-manager.hpp rename to applications/irda/irda-app-view-manager.h index 4569a104..1f38b472 100644 --- a/applications/irda/irda-app-view-manager.hpp +++ b/applications/irda/irda-app-view-manager.h @@ -6,7 +6,7 @@ #include #include #include -#include "irda-app.hpp" +#include "irda-app.h" #include "view/irda-app-brut-view.h" #include "gui/modules/button_panel.h" @@ -57,4 +57,3 @@ private: void add_view(ViewType view_type, View* view); }; - diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp index 1e2de687..5dd7c7b4 100644 --- a/applications/irda/irda-app.cpp +++ b/applications/irda/irda-app.cpp @@ -1,5 +1,5 @@ -#include "irda-app.hpp" -#include "irda/irda-app-file-parser.hpp" +#include "irda-app.h" +#include "irda/irda-app-file-parser.h" #include #include #include @@ -222,22 +222,33 @@ void IrdaApp::notify_click() { notification_message_block(notification, &sequence); } -void IrdaApp::notify_click_and_blink() { +void IrdaApp::notify_click_and_green_blink() { static const NotificationSequence sequence = { &message_click, &message_delay_1, &message_sound_off, - &message_red_0, &message_green_255, - &message_blue_0, &message_delay_10, &message_green_0, + &message_do_not_reset, NULL, }; notification_message_block(notification, &sequence); } +void IrdaApp::notify_blink_green() { + static const NotificationSequence sequence = { + &message_green_255, + &message_delay_10, + &message_green_0, + &message_do_not_reset, + NULL, + }; + + notification_message(notification, &sequence); +} + void IrdaApp::notify_double_vibro() { notification_message(notification, &sequence_double_vibro); } diff --git a/applications/irda/irda-app.hpp b/applications/irda/irda-app.h similarity index 90% rename from applications/irda/irda-app.hpp rename to applications/irda/irda-app.h index 0562aefb..cc7611a4 100644 --- a/applications/irda/irda-app.hpp +++ b/applications/irda/irda-app.h @@ -2,17 +2,16 @@ #include #include #include -#include "scene/irda-app-scene.hpp" -#include "irda-app-event.hpp" -#include "scene/irda-app-scene.hpp" -#include "irda-app-view-manager.hpp" -#include "irda-app-remote-manager.hpp" +#include "scene/irda-app-scene.h" +#include "irda-app-event.h" +#include "scene/irda-app-scene.h" +#include "irda-app-view-manager.h" +#include "irda-app-remote-manager.h" #include #include #include #include - class IrdaApp { public: enum class EditElement : uint8_t { @@ -71,7 +70,7 @@ public: void set_learn_new_remote(bool value); enum : int { - ButtonNA = -1, + ButtonNA = -1, }; int get_current_button(); void set_current_button(int value); @@ -83,7 +82,8 @@ public: void notify_green_on(); void notify_green_off(); void notify_click(); - void notify_click_and_blink(); + void notify_click_and_green_blink(); + void notify_blink_green(); static void text_input_callback(void* context); static void popup_callback(void* context); @@ -95,9 +95,9 @@ public: ~IrdaApp() { irda_worker_free(irda_worker); furi_record_close("notification"); - for (auto &it : scenes) - delete it.second; + for(auto& it : scenes) delete it.second; } + private: static const uint8_t text_store_size = 128; static const uint8_t text_store_max = 2; @@ -120,7 +120,7 @@ private: {Scene::Start, new IrdaAppSceneStart()}, {Scene::Universal, new IrdaAppSceneUniversal()}, {Scene::UniversalTV, new IrdaAppSceneUniversalTV()}, -// {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()}, + // {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()}, {Scene::Learn, new IrdaAppSceneLearn()}, {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, diff --git a/applications/irda/irda-runner.cpp b/applications/irda/irda-runner.cpp index bf9f51bc..cad6fd0a 100644 --- a/applications/irda/irda-runner.cpp +++ b/applications/irda/irda-runner.cpp @@ -1,4 +1,4 @@ -#include "irda-app.hpp" +#include "irda-app.h" extern "C" int32_t irda_app(void* p) { IrdaApp* app = new IrdaApp(); diff --git a/applications/irda/scene/irda-app-scene-edit-delete-done.cpp b/applications/irda/scene/irda-app-scene-edit-delete-done.cpp index fa5422dd..ddde3e7b 100644 --- a/applications/irda/scene/irda-app-scene-edit-delete-done.cpp +++ b/applications/irda/scene/irda-app-scene-edit-delete-done.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); diff --git a/applications/irda/scene/irda-app-scene-edit-delete.cpp b/applications/irda/scene/irda-app-scene-edit-delete.cpp index c16ee7f2..c5d1cdd7 100644 --- a/applications/irda/scene/irda-app-scene-edit-delete.cpp +++ b/applications/irda/scene/irda-app-scene-edit-delete.cpp @@ -1,6 +1,6 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "irda.h" -#include "irda/scene/irda-app-scene.hpp" +#include "irda/scene/irda-app-scene.h" #include static void dialog_result_callback(DialogExResult result, void* context) { diff --git a/applications/irda/scene/irda-app-scene-edit-key-select.cpp b/applications/irda/scene/irda-app-scene-edit-key-select.cpp index 41956b1d..8a6733d8 100644 --- a/applications/irda/scene/irda-app-scene-edit-key-select.cpp +++ b/applications/irda/scene/irda-app-scene-edit-key-select.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "gui/modules/submenu.h" static void submenu_callback(void* context, uint32_t index) { diff --git a/applications/irda/scene/irda-app-scene-edit-rename-done.cpp b/applications/irda/scene/irda-app-scene-edit-rename-done.cpp index 8e547f94..d3d135c7 100644 --- a/applications/irda/scene/irda-app-scene-edit-rename-done.cpp +++ b/applications/irda/scene/irda-app-scene-edit-rename-done.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); diff --git a/applications/irda/scene/irda-app-scene-edit-rename.cpp b/applications/irda/scene/irda-app-scene-edit-rename.cpp index 24b4d0f9..d821d03f 100644 --- a/applications/irda/scene/irda-app-scene-edit-rename.cpp +++ b/applications/irda/scene/irda-app-scene-edit-rename.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); diff --git a/applications/irda/scene/irda-app-scene-edit.cpp b/applications/irda/scene/irda-app-scene-edit.cpp index 0ecb8d9e..1b659313 100644 --- a/applications/irda/scene/irda-app-scene-edit.cpp +++ b/applications/irda/scene/irda-app-scene-edit.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "gui/modules/submenu.h" typedef enum { diff --git a/applications/irda/scene/irda-app-scene-learn-done.cpp b/applications/irda/scene/irda-app-scene-learn-done.cpp index a9bd2049..5a3bfcc7 100644 --- a/applications/irda/scene/irda-app-scene-learn-done.cpp +++ b/applications/irda/scene/irda-app-scene-learn-done.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); diff --git a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp index a3b403e0..185e7fbc 100644 --- a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp +++ b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "gui/modules/text_input.h" void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { diff --git a/applications/irda/scene/irda-app-scene-learn-success.cpp b/applications/irda/scene/irda-app-scene-learn-success.cpp index 4ade76df..8fa0c87b 100644 --- a/applications/irda/scene/irda-app-scene-learn-success.cpp +++ b/applications/irda/scene/irda-app-scene-learn-success.cpp @@ -1,6 +1,6 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "irda.h" -#include "../irda-app-file-parser.hpp" +#include "../irda-app-file-parser.h" #include static void dialog_result_callback(DialogExResult result, void* context) { @@ -51,6 +51,10 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; + if(event->type == IrdaAppEvent::Type::Tick) { + /* Send event every tick to suppress any switching off green light */ + app->notify_green_on(); + } if(event->type == IrdaAppEvent::Type::DialogExSelected) { switch(event->payload.dialog_ex_result) { diff --git a/applications/irda/scene/irda-app-scene-learn.cpp b/applications/irda/scene/irda-app-scene-learn.cpp index 7fdd8bf7..cbbbbe7a 100644 --- a/applications/irda/scene/irda-app-scene-learn.cpp +++ b/applications/irda/scene/irda-app-scene-learn.cpp @@ -1,5 +1,5 @@ -#include "../irda-app.hpp" -#include "../irda-app-event.hpp" +#include "../irda-app.h" +#include "../irda-app-event.h" #include static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { @@ -9,7 +9,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s IrdaApp* app = static_cast(context); if(irda_worker_signal_is_decoded(received_signal)) { - IrdaAppSignal signal(irda_worker_get_decoded_message(received_signal)); + IrdaAppSignal signal(irda_worker_get_decoded_signal(received_signal)); app->set_received_signal(signal); } else { const uint32_t* timings; @@ -19,7 +19,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s app->set_received_signal(signal); } - irda_worker_set_received_signal_callback(app->get_irda_worker(), NULL); + irda_worker_rx_set_received_signal_callback(app->get_irda_worker(), NULL, NULL); IrdaAppEvent event; event.type = IrdaAppEvent::Type::IrdaMessageReceived; auto view_manager = app->get_view_manager(); @@ -31,9 +31,8 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) { auto popup = view_manager->get_popup(); auto worker = app->get_irda_worker(); - irda_worker_set_context(worker, app); - irda_worker_set_received_signal_callback(worker, signal_received_callback); - irda_worker_start(worker); + irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, app); + irda_worker_rx_start(worker); popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31); popup_set_text( @@ -58,11 +57,9 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { case IrdaAppEvent::Type::IrdaMessageReceived: app->notify_success(); app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); - irda_worker_stop(app->get_irda_worker()); break; case IrdaAppEvent::Type::Back: consumed = true; - irda_worker_stop(app->get_irda_worker()); app->switch_to_previous_scene(); break; default: @@ -73,4 +70,5 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { } void IrdaAppSceneLearn::on_exit(IrdaApp* app) { + irda_worker_rx_stop(app->get_irda_worker()); } diff --git a/applications/irda/scene/irda-app-scene-remote-list.cpp b/applications/irda/scene/irda-app-scene-remote-list.cpp index d2b56e48..b6e8aab1 100644 --- a/applications/irda/scene/irda-app-scene-remote-list.cpp +++ b/applications/irda/scene/irda-app-scene-remote-list.cpp @@ -1,5 +1,5 @@ -#include "../irda-app.hpp" -#include "irda/irda-app-event.hpp" +#include "../irda-app.h" +#include "irda/irda-app-event.h" void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { IrdaAppFileParser file_parser; diff --git a/applications/irda/scene/irda-app-scene-remote.cpp b/applications/irda/scene/irda-app-scene-remote.cpp index 9260babe..94cf61ec 100644 --- a/applications/irda/scene/irda-app-scene-remote.cpp +++ b/applications/irda/scene/irda-app-scene-remote.cpp @@ -1,5 +1,7 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "gui/modules/button_menu.h" +#include "input/input.h" +#include "irda_worker.h" typedef enum { ButtonIndexPlus = -2, @@ -7,22 +9,41 @@ typedef enum { ButtonIndexNA = 0, } ButtonIndex; -static void button_menu_callback(void* context, int32_t index) { +static void button_menu_callback(void* context, int32_t index, InputType type) { IrdaApp* app = static_cast(context); IrdaAppEvent event; - event.type = IrdaAppEvent::Type::MenuSelected; + if(type == InputTypePress) { + event.type = IrdaAppEvent::Type::MenuSelectedPress; + } else if(type == InputTypeRelease) { + event.type = IrdaAppEvent::Type::MenuSelectedRelease; + } else if(type == InputTypeShort) { + event.type = IrdaAppEvent::Type::MenuSelected; + } else { + furi_assert(0); + } + event.payload.menu_index = index; app->get_view_manager()->send_event(&event); } +static void irda_app_message_sent_callback(void* context) { + IrdaApp* app = static_cast(context); + app->notify_blink_green(); +} + 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; + button_pressed = false; + irda_worker_tx_set_get_signal_callback( + app->get_irda_worker(), irda_worker_tx_get_signal_steady_callback, app); + irda_worker_tx_set_signal_sent_callback( + app->get_irda_worker(), irda_app_message_sent_callback, app); buttons_names = remote_manager->get_button_list(); i = 0; @@ -48,24 +69,49 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) { bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = true; - if(event->type == IrdaAppEvent::Type::MenuSelected) { + if((event->type == IrdaAppEvent::Type::MenuSelected) || + (event->type == IrdaAppEvent::Type::MenuSelectedPress) || + (event->type == IrdaAppEvent::Type::MenuSelectedRelease)) { switch(event->payload.menu_index) { case ButtonIndexPlus: + furi_assert(event->type == IrdaAppEvent::Type::MenuSelected); app->notify_click(); buttonmenu_item_selected = event->payload.menu_index; app->set_learn_new_remote(false); app->switch_to_next_scene(IrdaApp::Scene::Learn); break; case ButtonIndexEdit: + furi_assert(event->type == IrdaAppEvent::Type::MenuSelected); app->notify_click(); buttonmenu_item_selected = event->payload.menu_index; app->switch_to_next_scene(IrdaApp::Scene::Edit); break; default: - app->notify_click_and_blink(); - auto remote_manager = app->get_remote_manager(); - auto signal = remote_manager->get_button_data(event->payload.menu_index); - signal.transmit(); + furi_assert(event->type != IrdaAppEvent::Type::MenuSelected); + bool pressed = (event->type == IrdaAppEvent::Type::MenuSelectedPress); + + if(pressed && !button_pressed) { + button_pressed = true; + app->notify_click_and_green_blink(); + + auto button_signal = + app->get_remote_manager()->get_button_data(event->payload.menu_index); + if(button_signal.is_raw()) { + irda_worker_set_raw_signal( + app->get_irda_worker(), + button_signal.get_raw_signal().timings, + button_signal.get_raw_signal().timings_cnt); + } else { + irda_worker_set_decoded_signal( + app->get_irda_worker(), &button_signal.get_message()); + } + + irda_worker_tx_start(app->get_irda_worker()); + } else if(!pressed && button_pressed) { + button_pressed = false; + irda_worker_tx_stop(app->get_irda_worker()); + app->notify_green_off(); + } break; } } else if(event->type == IrdaAppEvent::Type::Back) { @@ -79,6 +125,8 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { } void IrdaAppSceneRemote::on_exit(IrdaApp* app) { + irda_worker_tx_set_get_signal_callback(app->get_irda_worker(), nullptr, nullptr); + irda_worker_tx_set_signal_sent_callback(app->get_irda_worker(), nullptr, nullptr); IrdaAppViewManager* view_manager = app->get_view_manager(); ButtonMenu* button_menu = view_manager->get_button_menu(); diff --git a/applications/irda/scene/irda-app-scene-start.cpp b/applications/irda/scene/irda-app-scene-start.cpp index 6d9e77f8..4b6bf13e 100644 --- a/applications/irda/scene/irda-app-scene-start.cpp +++ b/applications/irda/scene/irda-app-scene-start.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" typedef enum { SubmenuIndexUniversalLibrary, diff --git a/applications/irda/scene/irda-app-scene-universal-common.cpp b/applications/irda/scene/irda-app-scene-universal-common.cpp index 88e7cf34..8e2e3747 100644 --- a/applications/irda/scene/irda-app-scene-universal-common.cpp +++ b/applications/irda/scene/irda-app-scene-universal-common.cpp @@ -1,12 +1,12 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" #include "assets_icons.h" #include "gui/modules/button_menu.h" #include "gui/modules/button_panel.h" #include "../view/irda-app-brut-view.h" #include "gui/view.h" -#include "irda/irda-app-event.hpp" -#include "irda/irda-app-view-manager.hpp" -#include "irda/scene/irda-app-scene.hpp" +#include "irda/irda-app-event.h" +#include "irda/irda-app-view-manager.h" +#include "irda/scene/irda-app-scene.h" void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) { IrdaApp* app = static_cast(context); @@ -49,10 +49,11 @@ void IrdaAppSceneUniversalCommon::show_popup(IrdaApp* app, int record_amount) { button_panel_set_popup_input_callback(button_panel, irda_popup_brut_input_callback, app); } -void IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) { - popup_brut_increase_progress(app->get_view_manager()->get_popup_brut()); +bool IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) { + bool result = popup_brut_increase_progress(app->get_view_manager()->get_popup_brut()); auto button_panel = app->get_view_manager()->get_button_panel(); with_view_model_cpp(button_panel_get_view(button_panel), void*, model, { return true; }); + return result; } bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { @@ -63,9 +64,11 @@ bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { auto view_manager = app->get_view_manager(); IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; view_manager->send_event(&tick_event); - if(brute_force.send_next_bruteforce()) { - progress_popup(app); - } else { + bool result = brute_force.send_next_bruteforce(); + if(result) { + result = progress_popup(app); + } + if(!result) { brute_force.stop_bruteforce(); brute_force_started = false; remove_popup(app); diff --git a/applications/irda/scene/irda-app-scene-universal-tv.cpp b/applications/irda/scene/irda-app-scene-universal-tv.cpp index 6723990e..92a9ce37 100644 --- a/applications/irda/scene/irda-app-scene-universal-tv.cpp +++ b/applications/irda/scene/irda-app-scene-universal-tv.cpp @@ -1,5 +1,5 @@ -#include "irda/scene/irda-app-scene.hpp" -#include "irda/irda-app.hpp" +#include "irda/scene/irda-app-scene.h" +#include "irda/irda-app.h" void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); diff --git a/applications/irda/scene/irda-app-scene-universal.cpp b/applications/irda/scene/irda-app-scene-universal.cpp index b29adbe7..61a61fd2 100644 --- a/applications/irda/scene/irda-app-scene-universal.cpp +++ b/applications/irda/scene/irda-app-scene-universal.cpp @@ -1,4 +1,4 @@ -#include "../irda-app.hpp" +#include "../irda-app.h" typedef enum { SubmenuIndexUniversalTV, diff --git a/applications/irda/scene/irda-app-scene.hpp b/applications/irda/scene/irda-app-scene.h similarity index 90% rename from applications/irda/scene/irda-app-scene.hpp rename to applications/irda/scene/irda-app-scene.h index da80acfa..11eb1b38 100644 --- a/applications/irda/scene/irda-app-scene.hpp +++ b/applications/irda/scene/irda-app-scene.h @@ -1,11 +1,10 @@ #pragma once -#include "../irda-app-event.hpp" +#include "../irda-app-event.h" #include #include "irda.h" #include #include -#include "../irda-app-brute-force.hpp" - +#include "../irda-app-brute-force.h" class IrdaApp; @@ -24,6 +23,7 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; + private: uint32_t submenu_item_selected = 0; }; @@ -33,6 +33,7 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; + private: uint32_t submenu_item_selected = 0; }; @@ -70,9 +71,11 @@ 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; uint32_t buttonmenu_item_selected = 0; + bool button_pressed = false; }; class IrdaAppSceneRemoteList : public IrdaAppScene { @@ -80,6 +83,7 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; + private: uint32_t submenu_item_selected = 0; std::vector remote_names; @@ -90,6 +94,7 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; + private: uint32_t submenu_item_selected = 0; }; @@ -99,6 +104,7 @@ 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; }; @@ -133,16 +139,20 @@ public: class IrdaAppSceneUniversalCommon : public IrdaAppScene { bool brute_force_started = false; + protected: bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; IrdaAppBruteForce brute_force; void remove_popup(IrdaApp* app); void show_popup(IrdaApp* app, int record_amount); - void progress_popup(IrdaApp* app); + bool progress_popup(IrdaApp* app); static void irda_app_item_callback(void* context, uint32_t index); - IrdaAppSceneUniversalCommon(const char* filename) : brute_force(filename) {} - ~IrdaAppSceneUniversalCommon() {} + IrdaAppSceneUniversalCommon(const char* filename) + : brute_force(filename) { + } + ~IrdaAppSceneUniversalCommon() { + } }; class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon { @@ -151,13 +161,16 @@ public: IrdaAppSceneUniversalTV() : IrdaAppSceneUniversalCommon("/ext/irda/universal/tv.ir") { } - ~IrdaAppSceneUniversalTV() {} + ~IrdaAppSceneUniversalTV() { + } }; class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon { public: void on_enter(IrdaApp* app) final; - IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/ext/irda/universal/audio.ir") {} - ~IrdaAppSceneUniversalAudio() {} + IrdaAppSceneUniversalAudio() + : IrdaAppSceneUniversalCommon("/ext/irda/universal/audio.ir") { + } + ~IrdaAppSceneUniversalAudio() { + } }; - diff --git a/applications/irda/view/irda-app-brut-view.c b/applications/irda/view/irda-app-brut-view.c index 56936383..7f502464 100644 --- a/applications/irda/view/irda-app-brut-view.c +++ b/applications/irda/view/irda-app-brut-view.c @@ -15,13 +15,15 @@ struct IrdaAppPopupBrut { char percents_string_storage[8]; }; -void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut) { +bool popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut) { furi_assert(popup_brut); if(popup_brut->progress < popup_brut->progress_max) ++popup_brut->progress; else furi_assert(0); + + return popup_brut->progress < popup_brut->progress_max; } void popup_brut_draw_callback(Canvas* canvas, void* context) { diff --git a/applications/irda/view/irda-app-brut-view.h b/applications/irda/view/irda-app-brut-view.h index e9f0ec62..3f6b973a 100644 --- a/applications/irda/view/irda-app-brut-view.h +++ b/applications/irda/view/irda-app-brut-view.h @@ -7,7 +7,7 @@ extern "C" { typedef struct IrdaAppPopupBrut IrdaAppPopupBrut; -void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut); +bool popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut); IrdaAppPopupBrut* popup_brut_alloc(); void popup_brut_free(IrdaAppPopupBrut* popup_brut); void popup_brut_draw_callback(Canvas* canvas, void* model); diff --git a/applications/irda_monitor/irda_monitor.c b/applications/irda_monitor/irda_monitor.c index 8e3098cd..3a0a342a 100644 --- a/applications/irda_monitor/irda_monitor.c +++ b/applications/irda_monitor/irda_monitor.c @@ -58,7 +58,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s IrdaMonitor* irda_monitor = context; if(irda_worker_signal_is_decoded(received_signal)) { - const IrdaMessage* message = irda_worker_get_decoded_message(received_signal); + const IrdaMessage* message = irda_worker_get_decoded_signal(received_signal); snprintf( irda_monitor->display_text, sizeof(irda_monitor->display_text), @@ -112,10 +112,10 @@ int32_t irda_monitor_app(void* p) { gui_add_view_port(gui, irda_monitor->view_port, GuiLayerFullscreen); irda_monitor->worker = irda_worker_alloc(); - irda_worker_set_context(irda_monitor->worker, irda_monitor); - irda_worker_start(irda_monitor->worker); - irda_worker_set_received_signal_callback(irda_monitor->worker, signal_received_callback); - irda_worker_enable_blink_on_receiving(irda_monitor->worker, true); + irda_worker_rx_start(irda_monitor->worker); + irda_worker_rx_set_received_signal_callback( + irda_monitor->worker, signal_received_callback, irda_monitor); + irda_worker_rx_enable_blink_on_receiving(irda_monitor->worker, true); while(1) { InputEvent event; @@ -126,7 +126,7 @@ int32_t irda_monitor_app(void* p) { } } - irda_worker_stop(irda_monitor->worker); + irda_worker_rx_stop(irda_monitor->worker); irda_worker_free(irda_monitor->worker); osMessageQueueDelete(irda_monitor->event_queue); view_port_enabled_set(irda_monitor->view_port, false); diff --git a/firmware/targets/f6/furi-hal/furi-hal-irda.c b/firmware/targets/f6/furi-hal/furi-hal-irda.c index 0fe9b3cd..902f652f 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-irda.c +++ b/firmware/targets/f6/furi-hal/furi-hal-irda.c @@ -40,8 +40,10 @@ typedef struct{ typedef struct { float cycle_duration; - FuriHalIrdaTxGetDataCallback data_callback; + FuriHalIrdaTxGetDataISRCallback data_callback; + FuriHalIrdaTxSignalSentISRCallback signal_sent_callback; void* data_context; + void* signal_sent_context; IrdaTxBuf buffer[2]; osSemaphoreId_t stop_semaphore; } IrdaTimTx; @@ -175,8 +177,10 @@ void furi_hal_irda_async_rx_stop(void) { furi_hal_irda_state = IrdaStateIdle; } -void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_ms) { - LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); +void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_us) { + furi_assert(LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_TIM2)); + + LL_TIM_OC_SetCompareCH3(TIM2, timeout_us); LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); LL_TIM_EnableIT_CC3(TIM2); @@ -287,6 +291,9 @@ static void furi_hal_irda_tx_dma_isr() { /* if it's not end of the packet - continue receiving */ furi_hal_irda_tx_dma_set_buffer(next_buf_num); } + if (irda_tim_tx.signal_sent_callback) { + irda_tim_tx.signal_sent_callback(irda_tim_tx.signal_sent_context); + } } } @@ -576,9 +583,14 @@ void furi_hal_irda_async_tx_stop(void) { furi_hal_irda_async_tx_wait_termination(); } -void furi_hal_irda_async_tx_set_data_isr_callback(FuriHalIrdaTxGetDataCallback callback, void* context) { +void furi_hal_irda_async_tx_set_data_isr_callback(FuriHalIrdaTxGetDataISRCallback callback, void* context) { furi_assert(furi_hal_irda_state == IrdaStateIdle); irda_tim_tx.data_callback = callback; irda_tim_tx.data_context = context; } +void furi_hal_irda_async_tx_set_signal_sent_isr_callback(FuriHalIrdaTxSignalSentISRCallback callback, void* context) { + irda_tim_tx.signal_sent_callback = callback; + irda_tim_tx.signal_sent_context = context; +} + diff --git a/firmware/targets/furi-hal-include/furi-hal-irda.h b/firmware/targets/furi-hal-include/furi-hal-irda.h index 929fb613..6e490402 100644 --- a/firmware/targets/furi-hal-include/furi-hal-irda.h +++ b/firmware/targets/furi-hal-include/furi-hal-irda.h @@ -14,7 +14,15 @@ typedef enum { FuriHalIrdaTxGetDataStateLastDone, /* New data obtained, and this is end of package and no more data available */ } FuriHalIrdaTxGetDataState; -typedef FuriHalIrdaTxGetDataState (*FuriHalIrdaTxGetDataCallback) (void* context, uint32_t* duration, bool* level); +/* Callback type for providing data to IRDA DMA TX system. It is called every tim */ +typedef FuriHalIrdaTxGetDataState (*FuriHalIrdaTxGetDataISRCallback) (void* context, uint32_t* duration, bool* level); + +/* Callback type called every time signal is sent by DMA to Timer. + * Actually, it means there are 2 timings left to send for this signal, which is almost end. + * Don't use this callback to stop transmission, as far as there are next signal is + * charged for transmission by DMA. + */ +typedef void (*FuriHalIrdaTxSignalSentISRCallback) (void* context); /** * Signature of callback function for receiving continuous IRDA rx signal. @@ -44,16 +52,15 @@ void furi_hal_irda_async_rx_start(void); */ void furi_hal_irda_async_rx_stop(void); -/** Setup api hal for receiving silence timeout. +/** Setup hal for receiving silence timeout. * Should be used with 'furi_hal_irda_timeout_irq_set_callback()'. * - * @param[in] timeout_ms - time to wait for silence on IRDA port + * @param[in] timeout_us - time to wait for silence on IRDA port * before generating IRQ. */ -void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_ms); +void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_us); -/** - * Setup callback for previously initialized IRDA RX interrupt. +/** Setup callback for previously initialized IRDA RX interrupt. * * @param[in] callback - callback to call when RX signal edge changing occurs * @param[in] ctx - context for callback @@ -62,7 +69,7 @@ void furi_hal_irda_async_rx_set_capture_isr_callback(FuriHalIrdaRxCaptureCallbac /** * Setup callback for reaching silence timeout on IRDA port. - * Should setup api hal with 'furi_hal_irda_setup_rx_timeout_irq()' first. + * Should setup hal with 'furi_hal_irda_setup_rx_timeout_irq()' first. * * @param[in] callback - callback for silence timeout * @param[in] ctx - context to pass to callback @@ -82,7 +89,7 @@ bool furi_hal_irda_is_busy(void); * @param[in] callback - function to provide new data * @param[in] context - context for callback */ -void furi_hal_irda_async_tx_set_data_isr_callback(FuriHalIrdaTxGetDataCallback callback, void* context); +void furi_hal_irda_async_tx_set_data_isr_callback(FuriHalIrdaTxGetDataISRCallback callback, void* context); /** * Start IR asynchronous transmission. It can be stopped by 2 reasons: @@ -115,6 +122,14 @@ void furi_hal_irda_async_tx_stop(void); */ void furi_hal_irda_async_tx_wait_termination(void); +/** + * Set callback for end of signal transmission + * + * @param[in] callback - function to call when signal is sent + * @param[in] context - context for callback + */ +void furi_hal_irda_async_tx_set_signal_sent_isr_callback(FuriHalIrdaTxSignalSentISRCallback callback, void* context); + #ifdef __cplusplus } #endif diff --git a/lib/irda/encoder_decoder/irda.c b/lib/irda/encoder_decoder/irda.c index 9f5df893..6c7866d1 100644 --- a/lib/irda/encoder_decoder/irda.c +++ b/lib/irda/encoder_decoder/irda.c @@ -34,6 +34,8 @@ typedef struct { IrdaEncoders encoder; uint8_t address_length; uint8_t command_length; + uint32_t frequency; + float duty_cycle; } IrdaProtocolImplementation; struct IrdaEncoderHandler { @@ -58,6 +60,8 @@ static const IrdaProtocolImplementation irda_protocols[] = { .free = irda_encoder_nec_free}, .address_length = 2, .command_length = 2, + .frequency = IRDA_COMMON_CARRIER_FREQUENCY, + .duty_cycle = IRDA_COMMON_DUTY_CYCLE, }, // #1 - have to be after NEC { .protocol = IrdaProtocolNECext, @@ -74,6 +78,8 @@ static const IrdaProtocolImplementation irda_protocols[] = { .free = irda_encoder_nec_free}, .address_length = 4, .command_length = 2, + .frequency = IRDA_COMMON_CARRIER_FREQUENCY, + .duty_cycle = IRDA_COMMON_DUTY_CYCLE, }, // #2 { .protocol = IrdaProtocolSamsung32, @@ -90,6 +96,8 @@ static const IrdaProtocolImplementation irda_protocols[] = { .free = irda_encoder_samsung32_free}, .address_length = 2, .command_length = 2, + .frequency = IRDA_COMMON_CARRIER_FREQUENCY, + .duty_cycle = IRDA_COMMON_DUTY_CYCLE, }, // #3 { .protocol = IrdaProtocolRC6, @@ -106,6 +114,8 @@ static const IrdaProtocolImplementation irda_protocols[] = { .free = irda_encoder_rc6_free}, .address_length = 2, .command_length = 2, + .frequency = IRDA_COMMON_CARRIER_FREQUENCY, + .duty_cycle = IRDA_COMMON_DUTY_CYCLE, }, }; @@ -222,10 +232,12 @@ IrdaProtocol irda_get_protocol_by_name(const char* protocol_name) { if (!strcmp(irda_protocols[i].name, protocol_name)) return i; } + furi_assert(0); return IrdaProtocolUnknown; } const char* irda_get_protocol_name(IrdaProtocol protocol) { + furi_assert(irda_is_protocol_valid(protocol)); if (irda_is_protocol_valid(protocol)) return irda_protocols[protocol].name; else @@ -233,6 +245,7 @@ const char* irda_get_protocol_name(IrdaProtocol protocol) { } uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { + furi_assert(irda_is_protocol_valid(protocol)); if (irda_is_protocol_valid(protocol)) return irda_protocols[protocol].address_length; else @@ -240,9 +253,26 @@ uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { } uint8_t irda_get_protocol_command_length(IrdaProtocol protocol) { + furi_assert(irda_is_protocol_valid(protocol)); if (irda_is_protocol_valid(protocol)) return irda_protocols[protocol].command_length; else return 0; } +uint32_t irda_get_protocol_frequency(IrdaProtocol protocol) { + furi_assert(irda_is_protocol_valid(protocol)); + if (irda_is_protocol_valid(protocol)) + return irda_protocols[protocol].frequency; + else + return 0; +} + +float irda_get_protocol_duty_cycle(IrdaProtocol protocol) { + furi_assert(irda_is_protocol_valid(protocol)); + if (irda_is_protocol_valid(protocol)) + return irda_protocols[protocol].duty_cycle; + else + return 0; +} + diff --git a/lib/irda/encoder_decoder/irda.h b/lib/irda/encoder_decoder/irda.h index 2c3a7ac1..954be818 100644 --- a/lib/irda/encoder_decoder/irda.h +++ b/lib/irda/encoder_decoder/irda.h @@ -10,6 +10,12 @@ extern "C" { #define IRDA_COMMON_CARRIER_FREQUENCY 38000 #define IRDA_COMMON_DUTY_CYCLE 0.33 +/* if we want to see splitted raw signals during brutforce, + * we have to have RX raw timing delay less than TX */ +#define IRDA_RAW_RX_TIMING_DELAY_US 150000 +#define IRDA_RAW_TX_TIMING_DELAY_US 180000 + + typedef struct IrdaDecoderHandler IrdaDecoderHandler; typedef struct IrdaEncoderHandler IrdaEncoderHandler; @@ -150,6 +156,24 @@ IrdaStatus irda_encode(IrdaEncoderHandler* handler, uint32_t* duration, bool* le */ void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message); +/** + * Get PWM frequency value for selected protocol + * + * \param[in] protocol - protocol to get from PWM frequency + * + * \return frequency + */ +uint32_t irda_get_protocol_frequency(IrdaProtocol protocol); + +/** + * Get PWM duty cycle value for selected protocol + * + * \param[in] protocol - protocol to get from PWM duty cycle + * + * \return duty cycle + */ +float irda_get_protocol_duty_cycle(IrdaProtocol protocol); + #ifdef __cplusplus } #endif diff --git a/lib/irda/encoder_decoder/irda_protocol_defs_i.h b/lib/irda/encoder_decoder/irda_protocol_defs_i.h index ee2320a4..9c4e5ae9 100644 --- a/lib/irda/encoder_decoder/irda_protocol_defs_i.h +++ b/lib/irda/encoder_decoder/irda_protocol_defs_i.h @@ -137,7 +137,8 @@ extern const IrdaCommonProtocolSpec protocol_samsung32; #define IRDA_RC6_BIT 444 // half of time-quant for 1 bit #define IRDA_RC6_PREAMBLE_TOLERANCE 0.07 // percents #define IRDA_RC6_BIT_TOLERANCE 120 // us -#define IRDA_RC6_SILENCE 2700 +/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ +#define IRDA_RC6_SILENCE (2700 * 10) void* irda_decoder_rc6_alloc(void); void irda_decoder_rc6_reset(void* decoder); diff --git a/lib/irda/worker/irda_transmit.c b/lib/irda/worker/irda_transmit.c index 89b193e4..e87de6ca 100644 --- a/lib/irda/worker/irda_transmit.c +++ b/lib/irda/worker/irda_transmit.c @@ -23,7 +23,7 @@ FuriHalIrdaTxGetDataState irda_get_raw_data_callback (void* context, uint32_t* d if (irda_tx_raw_add_silence && (irda_tx_raw_timings_index == 0)) { irda_tx_raw_add_silence = false; *level = false; - *duration = 180000; // 180 ms delay between raw packets + *duration = IRDA_RAW_TX_TIMING_DELAY_US; } else { *level = irda_tx_raw_start_from_mark ^ (irda_tx_raw_timings_index % 2); *duration = timings[irda_tx_raw_timings_index++]; diff --git a/lib/irda/worker/irda_worker.c b/lib/irda/worker/irda_worker.c index da8b72fc..041ea090 100644 --- a/lib/irda/worker/irda_worker.c +++ b/lib/irda/worker/irda_worker.c @@ -1,20 +1,45 @@ +#include "furi/check.h" +#include "furi/common_defines.h" +#include "sys/_stdint.h" #include "irda_worker.h" #include #include #include #include -#include #include #include +#include + +#define IRDA_WORKER_RX_TIMEOUT IRDA_RAW_RX_TIMING_DELAY_US -#define MAX_TIMINGS_AMOUNT 500 -#define IRDA_WORKER_RX_TIMEOUT 150 // ms #define IRDA_WORKER_RX_RECEIVED 0x01 #define IRDA_WORKER_RX_TIMEOUT_RECEIVED 0x02 #define IRDA_WORKER_OVERRUN 0x04 #define IRDA_WORKER_EXIT 0x08 +#define IRDA_WORKER_TX_FILL_BUFFER 0x10 +#define IRDA_WORKER_TX_MESSAGE_SENT 0x20 -struct IrdaWorkerSignal { +#define IRDA_WORKER_ALL_RX_EVENTS (IRDA_WORKER_RX_RECEIVED \ + | IRDA_WORKER_RX_TIMEOUT_RECEIVED \ + | IRDA_WORKER_OVERRUN \ + | IRDA_WORKER_EXIT) + +#define IRDA_WORKER_ALL_TX_EVENTS (IRDA_WORKER_TX_FILL_BUFFER \ + | IRDA_WORKER_TX_MESSAGE_SENT \ + | IRDA_WORKER_EXIT) + +#define IRDA_WORKER_ALL_EVENTS (IRDA_WORKER_ALL_RX_EVENTS | IRDA_WORKER_ALL_TX_EVENTS) + +typedef enum { + IrdaWorkerStateIdle, + IrdaWorkerStateRunRx, + IrdaWorkerStateRunTx, + IrdaWorkerStateWaitTxEnd, + IrdaWorkerStateStopTx, + IrdaWorkerStateStartTx, +} IrdaWorkerState; + +struct IrdaWorkerSignal{ bool decoded; size_t timings_cnt; union { @@ -25,37 +50,67 @@ struct IrdaWorkerSignal { struct IrdaWorker { FuriThread* thread; - IrdaDecoderHandler* irda_decoder; StreamBufferHandle_t stream; + osEventFlagsId_t events; - TaskHandle_t worker_handle; IrdaWorkerSignal signal; - - IrdaWorkerReceivedSignalCallback received_signal_callback; - void* context; - bool blink_enable; - bool overrun; + IrdaWorkerState state; + IrdaEncoderHandler* irda_encoder; + IrdaDecoderHandler* irda_decoder; NotificationApp* notification; + bool blink_enable; + + union { + struct { + IrdaWorkerGetSignalCallback get_signal_callback; + IrdaWorkerMessageSentCallback message_sent_callback; + void* get_signal_context; + void* message_sent_context; + uint32_t frequency; + float duty_cycle; + uint32_t tx_raw_cnt; + bool need_reinitialization; + bool steady_signal_sent; + } tx; + struct { + IrdaWorkerReceivedSignalCallback received_signal_callback; + void* received_signal_context; + bool overrun; + } rx; + }; }; +typedef struct { + uint32_t duration; + bool level; + FuriHalIrdaTxGetDataState state; +} IrdaWorkerTiming; + +static int32_t irda_worker_tx_thread(void* context); +static FuriHalIrdaTxGetDataState irda_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level); +static void irda_worker_furi_hal_message_sent_isr_callback(void* context); + + static void irda_worker_rx_timeout_callback(void* context) { IrdaWorker* instance = context; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xTaskNotifyFromISR(instance->worker_handle, IRDA_WORKER_RX_TIMEOUT_RECEIVED, eSetBits, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_RX_TIMEOUT_RECEIVED); + furi_check(flags_set & IRDA_WORKER_RX_TIMEOUT_RECEIVED); } static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) { IrdaWorker* instance = context; BaseType_t xHigherPriorityTaskWoken = pdFALSE; + furi_assert(duration != 0); LevelDuration level_duration = level_duration_make(level, duration); size_t ret = xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); - uint32_t notify_value = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN; - xTaskNotifyFromISR(instance->worker_handle, notify_value, eSetBits, &xHigherPriorityTaskWoken); + uint32_t events = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN; portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + + uint32_t flags_set = osEventFlagsSet(instance->events, events); + furi_check(flags_set & events); } static void irda_worker_process_timeout(IrdaWorker* instance) { @@ -63,8 +118,8 @@ static void irda_worker_process_timeout(IrdaWorker* instance) { return; instance->signal.decoded = false; - if (instance->received_signal_callback) - instance->received_signal_callback(instance->context, &instance->signal); + if (instance->rx.received_signal_callback) + instance->rx.received_signal_callback(instance->rx.received_signal_context, &instance->signal); } static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, bool level) { @@ -73,8 +128,8 @@ static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, instance->signal.data.message = *message_decoded; instance->signal.timings_cnt = 0; instance->signal.decoded = true; - if (instance->received_signal_callback) - instance->received_signal_callback(instance->context, &instance->signal); + if (instance->rx.received_signal_callback) + instance->rx.received_signal_callback(instance->rx.received_signal_context, &instance->signal); } else { /* Skip first timing if it's starts from Space */ if ((instance->signal.timings_cnt == 0) && !level) { @@ -85,50 +140,49 @@ static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, instance->signal.data.timings[instance->signal.timings_cnt] = duration; ++instance->signal.timings_cnt; } else { - xTaskNotify(instance->worker_handle, IRDA_WORKER_OVERRUN, eSetBits); - instance->overrun = true; + uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_OVERRUN); + furi_check(flags_set & IRDA_WORKER_OVERRUN); + instance->rx.overrun = true; } } } -static int32_t irda_worker_thread_callback(void* context) { - IrdaWorker* instance = context; - uint32_t notify_value = 0; +static int32_t irda_worker_rx_thread(void* thread_context) { + IrdaWorker* instance = thread_context; + uint32_t events = 0; LevelDuration level_duration; TickType_t last_blink_time = 0; while(1) { - BaseType_t result; - result = xTaskNotifyWait(pdFALSE, ULONG_MAX, ¬ify_value, 1000); - if (result != pdPASS) - continue; + events = osEventFlagsWait(instance->events, IRDA_WORKER_ALL_RX_EVENTS, 0, osWaitForever); + furi_check(events & IRDA_WORKER_ALL_RX_EVENTS); /* at least one caught */ - if (notify_value & IRDA_WORKER_RX_RECEIVED) { - if (!instance->overrun && instance->blink_enable && ((xTaskGetTickCount() - last_blink_time) > 80)) { + if (events & IRDA_WORKER_RX_RECEIVED) { + if (!instance->rx.overrun && instance->blink_enable && ((xTaskGetTickCount() - last_blink_time) > 80)) { last_blink_time = xTaskGetTickCount(); notification_message(instance->notification, &sequence_blink_blue_10); } if (instance->signal.timings_cnt == 0) notification_message(instance->notification, &sequence_display_on); while (sizeof(LevelDuration) == xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 0)) { - if (!instance->overrun) { + if (!instance->rx.overrun) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); irda_worker_process_timings(instance, duration, level); } } } - if (notify_value & IRDA_WORKER_OVERRUN) { + if (events & IRDA_WORKER_OVERRUN) { printf("#"); irda_reset_decoder(instance->irda_decoder); instance->signal.timings_cnt = 0; if (instance->blink_enable) notification_message(instance->notification, &sequence_set_red_255); } - if (notify_value & IRDA_WORKER_RX_TIMEOUT_RECEIVED) { - if (instance->overrun) { + if (events & IRDA_WORKER_RX_TIMEOUT_RECEIVED) { + if (instance->rx.overrun) { printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT); - instance->overrun = false; + instance->rx.overrun = false; if (instance->blink_enable) notification_message(instance->notification, &sequence_reset_red); } else { @@ -136,16 +190,17 @@ static int32_t irda_worker_thread_callback(void* context) { } instance->signal.timings_cnt = 0; } - if (notify_value & IRDA_WORKER_EXIT) + if (events & IRDA_WORKER_EXIT) break; } return 0; } -void irda_worker_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback) { +void irda_worker_rx_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback, void* context) { furi_assert(instance); - instance->received_signal_callback = callback; + instance->rx.received_signal_callback = callback; + instance->rx.received_signal_context = context; } IrdaWorker* irda_worker_alloc() { @@ -155,60 +210,67 @@ IrdaWorker* irda_worker_alloc() { furi_thread_set_name(instance->thread, "irda_worker"); furi_thread_set_stack_size(instance->thread, 2048); furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, irda_worker_thread_callback); - - instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 512, sizeof(LevelDuration)); + size_t buffer_size = MAX(sizeof(IrdaWorkerTiming) * MAX_TIMINGS_AMOUNT, sizeof(LevelDuration) * MAX_TIMINGS_AMOUNT); + instance->stream = xStreamBufferCreate(buffer_size, sizeof(IrdaWorkerTiming)); instance->irda_decoder = irda_alloc_decoder(); + instance->irda_encoder = irda_alloc_encoder(); instance->blink_enable = false; instance->notification = furi_record_open("notification"); + instance->state = IrdaWorkerStateIdle; + instance->events = osEventFlagsNew(NULL); return instance; } void irda_worker_free(IrdaWorker* instance) { furi_assert(instance); - furi_assert(!instance->worker_handle); + furi_assert(instance->state == IrdaWorkerStateIdle); furi_record_close("notification"); irda_free_decoder(instance->irda_decoder); + irda_free_encoder(instance->irda_encoder); vStreamBufferDelete(instance->stream); furi_thread_free(instance->thread); + osEventFlagsDelete(instance->events); free(instance); } -void irda_worker_set_context(IrdaWorker* instance, void* context) { +void irda_worker_rx_start(IrdaWorker* instance) { furi_assert(instance); - instance->context = context; -} + furi_assert(instance->state == IrdaWorkerStateIdle); -void irda_worker_start(IrdaWorker* instance) { - furi_assert(instance); - furi_assert(!instance->worker_handle); + xStreamBufferSetTriggerLevel(instance->stream, sizeof(LevelDuration)); + osEventFlagsClear(instance->events, IRDA_WORKER_ALL_EVENTS); + furi_thread_set_callback(instance->thread, irda_worker_rx_thread); furi_thread_start(instance->thread); - instance->worker_handle = furi_thread_get_thread_id(instance->thread); - furi_hal_irda_async_rx_start(); - furi_hal_irda_async_rx_set_timeout(IRDA_WORKER_RX_TIMEOUT); furi_hal_irda_async_rx_set_capture_isr_callback(irda_worker_rx_callback, instance); furi_hal_irda_async_rx_set_timeout_isr_callback(irda_worker_rx_timeout_callback, instance); + furi_hal_irda_async_rx_start(); + furi_hal_irda_async_rx_set_timeout(IRDA_WORKER_RX_TIMEOUT); + + instance->state = IrdaWorkerStateRunRx; } -void irda_worker_stop(IrdaWorker* instance) { +void irda_worker_rx_stop(IrdaWorker* instance) { furi_assert(instance); - furi_assert(instance->worker_handle); + furi_assert(instance->state == IrdaWorkerStateRunRx); furi_hal_irda_async_rx_set_timeout_isr_callback(NULL, NULL); furi_hal_irda_async_rx_set_capture_isr_callback(NULL, NULL); furi_hal_irda_async_rx_stop(); - xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits); - - instance->worker_handle = NULL; - + osEventFlagsSet(instance->events, IRDA_WORKER_EXIT); furi_thread_join(instance->thread); + + BaseType_t xReturn = pdFAIL; + xReturn = xStreamBufferReset(instance->stream); + furi_assert(xReturn == pdPASS); + instance->state = IrdaWorkerStateIdle; + instance->state = IrdaWorkerStateIdle; } bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal) { @@ -225,13 +287,268 @@ void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** *timings_cnt = signal->timings_cnt; } -const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal) { +const IrdaMessage* irda_worker_get_decoded_signal(const IrdaWorkerSignal* signal) { furi_assert(signal); return &signal->data.message; } -void irda_worker_enable_blink_on_receiving(IrdaWorker* instance, bool enable) { +void irda_worker_rx_enable_blink_on_receiving(IrdaWorker* instance, bool enable) { furi_assert(instance); instance->blink_enable = enable; } +void irda_worker_tx_start(IrdaWorker* instance) { + furi_assert(instance); + furi_assert(instance->state == IrdaWorkerStateIdle); + + // size have to be greater than api hal irda async tx buffer size + xStreamBufferSetTriggerLevel(instance->stream, sizeof(IrdaWorkerTiming)); + + osEventFlagsClear(instance->events, IRDA_WORKER_ALL_EVENTS); + furi_thread_set_callback(instance->thread, irda_worker_tx_thread); + furi_thread_start(instance->thread); + + instance->tx.steady_signal_sent = false; + instance->tx.need_reinitialization = false; + furi_hal_irda_async_tx_set_data_isr_callback(irda_worker_furi_hal_data_isr_callback, instance); + furi_hal_irda_async_tx_set_signal_sent_isr_callback(irda_worker_furi_hal_message_sent_isr_callback, instance); + + instance->state = IrdaWorkerStateStartTx; +} + +static void irda_worker_furi_hal_message_sent_isr_callback(void* context) { + IrdaWorker* instance = context; + uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_TX_MESSAGE_SENT); + furi_check(flags_set & IRDA_WORKER_TX_MESSAGE_SENT); +} + +static FuriHalIrdaTxGetDataState irda_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level) { + furi_assert(context); + furi_assert(duration); + furi_assert(level); + + IrdaWorker* instance = context; + IrdaWorkerTiming timing = {.state = FuriHalIrdaTxGetDataStateError} ; + + if (sizeof(IrdaWorkerTiming) == xStreamBufferReceiveFromISR(instance->stream, &timing, sizeof(IrdaWorkerTiming), 0)) { + *level = timing.level; + *duration = timing.duration; + furi_assert(timing.state != FuriHalIrdaTxGetDataStateError); + } else { + furi_assert(0); + timing.state = FuriHalIrdaTxGetDataStateError; + } + + uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_TX_FILL_BUFFER); + furi_check(flags_set & IRDA_WORKER_TX_FILL_BUFFER); + + return timing.state; +} + +static bool irda_get_new_signal(IrdaWorker* instance) { + bool new_signal_obtained = false; + + IrdaWorkerGetSignalResponse response = instance->tx.get_signal_callback(instance->tx.get_signal_context, instance); + if (response == IrdaWorkerGetSignalResponseNew) { + uint32_t new_tx_frequency = 0; + float new_tx_duty_cycle = 0; + if (instance->signal.decoded) { + new_tx_frequency = irda_get_protocol_frequency(instance->signal.data.message.protocol); + new_tx_duty_cycle = irda_get_protocol_duty_cycle(instance->signal.data.message.protocol); + } else { + furi_assert(instance->signal.timings_cnt > 1); + new_tx_frequency = IRDA_COMMON_CARRIER_FREQUENCY; + new_tx_duty_cycle = IRDA_COMMON_DUTY_CYCLE; + } + + instance->tx.tx_raw_cnt = 0; + instance->tx.need_reinitialization = (new_tx_frequency != instance->tx.frequency) || (new_tx_duty_cycle != instance->tx.duty_cycle); + instance->tx.frequency = new_tx_frequency; + instance->tx.duty_cycle = new_tx_duty_cycle; + if (instance->signal.decoded) { + irda_reset_encoder(instance->irda_encoder, &instance->signal.data.message); + } + new_signal_obtained = true; + } else if (response == IrdaWorkerGetSignalResponseSame) { + new_signal_obtained = true; + /* no need to reinit */ + } else if (response == IrdaWorkerGetSignalResponseStop) { + new_signal_obtained = false; + } else { + furi_assert(0); + } + + return new_signal_obtained; +} + +static bool irda_worker_tx_fill_buffer(IrdaWorker* instance) { + bool new_data_available = true; + IrdaWorkerTiming timing; + IrdaStatus status = IrdaStatusError; + + while(!xStreamBufferIsFull(instance->stream) && !instance->tx.need_reinitialization && new_data_available) { + if (instance->signal.decoded) { + status = irda_encode(instance->irda_encoder, &timing.duration, &timing.level); + } else { + timing.duration = instance->signal.data.timings[instance->tx.tx_raw_cnt]; +/* raw always starts from Mark, but we fulfill it with space delay at start */ + timing.level = (instance->tx.tx_raw_cnt % 2); + ++instance->tx.tx_raw_cnt; + if (instance->tx.tx_raw_cnt >= instance->signal.timings_cnt) { + instance->tx.tx_raw_cnt = 0; + status = IrdaStatusDone; + } else { + status = IrdaStatusOk; + } + } + + if (status == IrdaStatusError) { + furi_assert(0); + new_data_available = false; + break; + } else if (status == IrdaStatusOk) { + timing.state = FuriHalIrdaTxGetDataStateOk; + } else if (status == IrdaStatusDone) { + timing.state = FuriHalIrdaTxGetDataStateDone; + + new_data_available = irda_get_new_signal(instance); + if (instance->tx.need_reinitialization || !new_data_available) { + timing.state = FuriHalIrdaTxGetDataStateLastDone; + } + } else { + furi_assert(0); + } + uint32_t written_size = xStreamBufferSend(instance->stream, &timing, sizeof(IrdaWorkerTiming), 0); + furi_assert(sizeof(IrdaWorkerTiming) == written_size); + } + + return new_data_available; +} + +static int32_t irda_worker_tx_thread(void* thread_context) { + IrdaWorker* instance = thread_context; + furi_assert(instance->state == IrdaWorkerStateStartTx); + furi_assert(thread_context); + + uint32_t events = 0; + bool new_data_available = true; + bool exit = false; + + exit = !irda_get_new_signal(instance); + furi_assert(!exit); + + while(!exit) { + switch (instance->state) { + case IrdaWorkerStateStartTx: + instance->tx.need_reinitialization = false; + new_data_available = irda_worker_tx_fill_buffer(instance); + furi_hal_irda_async_tx_start(instance->tx.frequency, instance->tx.duty_cycle); + + if (!new_data_available) { + instance->state = IrdaWorkerStateStopTx; + } else if (instance->tx.need_reinitialization) { + instance->state = IrdaWorkerStateWaitTxEnd; + } else { + instance->state = IrdaWorkerStateRunTx; + } + + break; + case IrdaWorkerStateStopTx: + furi_hal_irda_async_tx_stop(); + exit = true; + break; + case IrdaWorkerStateWaitTxEnd: + furi_hal_irda_async_tx_wait_termination(); + instance->state = IrdaWorkerStateStartTx; + + events = osEventFlagsGet(instance->events); + if(events & IRDA_WORKER_EXIT) { + exit = true; + break; + } + + break; + case IrdaWorkerStateRunTx: + events = osEventFlagsWait(instance->events, IRDA_WORKER_ALL_TX_EVENTS, 0, osWaitForever); + furi_check(events & IRDA_WORKER_ALL_TX_EVENTS); /* at least one caught */ + + if (events & IRDA_WORKER_EXIT) { + instance->state = IrdaWorkerStateStopTx; + break; + } + + if (events & IRDA_WORKER_TX_FILL_BUFFER) { + irda_worker_tx_fill_buffer(instance); + + if (instance->tx.need_reinitialization) { + instance->state = IrdaWorkerStateWaitTxEnd; + } + } + + if (events & IRDA_WORKER_TX_MESSAGE_SENT) { + if (instance->tx.message_sent_callback) + instance->tx.message_sent_callback(instance->tx.message_sent_context); + } + break; + default: + furi_assert(0); + break; + } + } + + return 0; +} + +void irda_worker_tx_set_get_signal_callback(IrdaWorker* instance, IrdaWorkerGetSignalCallback callback, void* context) { + furi_assert(instance); + instance->tx.get_signal_callback = callback; + instance->tx.get_signal_context = context; +} + +void irda_worker_tx_set_signal_sent_callback(IrdaWorker* instance, IrdaWorkerMessageSentCallback callback, void* context) { + furi_assert(instance); + instance->tx.message_sent_callback = callback; + instance->tx.message_sent_context = context; +} + +void irda_worker_tx_stop(IrdaWorker* instance) { + furi_assert(instance); + furi_assert(instance->state != IrdaWorkerStateRunRx); + + osEventFlagsSet(instance->events, IRDA_WORKER_EXIT); + furi_thread_join(instance->thread); + furi_hal_irda_async_tx_set_data_isr_callback(NULL, NULL); + furi_hal_irda_async_tx_set_signal_sent_isr_callback(NULL, NULL); + + instance->signal.timings_cnt = 0; + BaseType_t xReturn = pdFAIL; + xReturn = xStreamBufferReset(instance->stream); + furi_assert(xReturn == pdPASS); + instance->state = IrdaWorkerStateIdle; +} + +void irda_worker_set_decoded_signal(IrdaWorker* instance, const IrdaMessage* message) { + furi_assert(instance); + furi_assert(message); + + instance->signal.decoded = true; + instance->signal.data.message = *message; +} + +void irda_worker_set_raw_signal(IrdaWorker* instance, const uint32_t* timings, size_t timings_cnt) { + furi_assert(instance); + furi_assert(timings); + furi_assert(timings_cnt > 2); + + instance->signal.data.timings[0] = IRDA_RAW_TX_TIMING_DELAY_US; + memcpy(&instance->signal.data.timings[1], timings, timings_cnt * sizeof(uint32_t)); + instance->signal.decoded = false; + instance->signal.timings_cnt = timings_cnt + 1; +} + +IrdaWorkerGetSignalResponse irda_worker_tx_get_signal_steady_callback(void* context, IrdaWorker* instance) { + IrdaWorkerGetSignalResponse response = instance->tx.steady_signal_sent ? IrdaWorkerGetSignalResponseSame : IrdaWorkerGetSignalResponseNew; + instance->tx.steady_signal_sent = true; + return response; +} + diff --git a/lib/irda/worker/irda_worker.h b/lib/irda/worker/irda_worker.h index 92b48725..90452bb3 100644 --- a/lib/irda/worker/irda_worker.h +++ b/lib/irda/worker/irda_worker.h @@ -7,11 +7,28 @@ extern "C" { #endif +#define MAX_TIMINGS_AMOUNT 512 + /** Interface struct of irda worker */ typedef struct IrdaWorker IrdaWorker; /** Interface struct of received signal */ typedef struct IrdaWorkerSignal IrdaWorkerSignal; +typedef enum { + IrdaWorkerGetSignalResponseNew, /** Signal, provided by callback is new and encoder should be reseted */ + IrdaWorkerGetSignalResponseSame, /** Signal, provided by callback is same. No encoder resetting. */ + IrdaWorkerGetSignalResponseStop, /** No more signals available. */ +} IrdaWorkerGetSignalResponse; + +/** Callback type for providing next signal to send. Should be used with + * irda_worker_make_decoded_signal() or irda_worker_make_raw_signal() + */ +typedef IrdaWorkerGetSignalResponse (*IrdaWorkerGetSignalCallback)(void* context, IrdaWorker* instance); + +/** Callback type for 'message is sent' event */ +typedef void (*IrdaWorkerMessageSentCallback)(void* context); + + /** Callback type to call by IrdaWorker thread when new signal is received */ typedef void (*IrdaWorkerReceivedSignalCallback)(void* context, IrdaWorkerSignal* received_signal); @@ -27,31 +44,33 @@ IrdaWorker* irda_worker_alloc(); */ void irda_worker_free(IrdaWorker* instance); -/** Received data callback IrdaWorker - * - * @param[in] instance - IrdaWorker instance - * @param[in] callback - IrdaWorkerReceivedSignalCallback callback - */ -void irda_worker_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback); - -/** Context callback IrdaWorker - * - * @param[in] instance - IrdaWorker instance - * @param[in] context - context to pass to callbacks - */ -void irda_worker_set_context(IrdaWorker* instance, void* context); - /** Start IrdaWorker thread, initialise furi-hal, prepare all work. * * @param[in] instance - IrdaWorker instance */ -void irda_worker_start(IrdaWorker* instance); +void irda_worker_rx_start(IrdaWorker* instance); /** Stop IrdaWorker thread, deinitialize furi-hal. * * @param[in] instance - IrdaWorker instance */ -void irda_worker_stop(IrdaWorker* instance); +void irda_worker_rx_stop(IrdaWorker* instance); + +/** Set received data callback IrdaWorker + * + * @param[in] instance - IrdaWorker instance + * @param[in] context - context to pass to callbacks + * @param[in] callback - IrdaWorkerReceivedSignalCallback callback + */ +void irda_worker_rx_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback, void* context); + +/** Enable blinking on receiving any signal on IR port. + * + * @param[in] instance - instance of IrdaWorker + * @param[in] enable - true if you want to enable blinking + * false otherwise + */ +void irda_worker_rx_enable_blink_on_receiving(IrdaWorker* instance, bool enable); /** Clarify is received signal either decoded or raw * @@ -60,6 +79,48 @@ void irda_worker_stop(IrdaWorker* instance); */ bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal); +/** Start transmitting signal. Callback IrdaWorkerGetSignalCallback should be + * set before this function is called, as it calls for it to fill buffer before + * starting transmission. + * + * @param[in] instance - IrdaWorker instance + */ +void irda_worker_tx_start(IrdaWorker* instance); + +/** Stop transmitting signal. Waits for end of current signal and stops transmission. + * + * @param[in] instance - IrdaWorker instance + */ +void irda_worker_tx_stop(IrdaWorker* instance); + +/** Set callback for providing next signal to send + * + * @param[in] instance - IrdaWorker instance + * @param[in] context - context to pass to callbacks + * @param[in] callback - IrdaWorkerGetSignalCallback callback + */ +void irda_worker_tx_set_get_signal_callback(IrdaWorker* instance, IrdaWorkerGetSignalCallback callback, void* context); + +/** Set callback for end of signal transmitting + * + * @param[in] instance - IrdaWorker instance + * @param[in] context - context to pass to callbacks + * @param[in] callback - IrdaWorkerMessageSentCallback callback + */ +void irda_worker_tx_set_signal_sent_callback(IrdaWorker* instance, IrdaWorkerMessageSentCallback callback, void* context); + +/** Callback to pass to irda_worker_tx_set_get_signal_callback() if signal + * is steady and will not be changed between irda_worker start and stop. + * Before starting transmission, desired steady signal must be set with + * irda_worker_make_decoded_signal() or irda_worker_make_raw_signal(). + * + * This function should not be implicitly called. + * + * @param[in] context - context + * @param[out] instance - IrdaWorker instance + */ +IrdaWorkerGetSignalResponse irda_worker_tx_get_signal_steady_callback(void* context, IrdaWorker* instance); + /** Acquire raw signal from interface struct 'IrdaWorkerSignal'. * First, you have to ensure that signal is raw. * @@ -73,17 +134,24 @@ void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** * First, you have to ensure that signal is decoded. * * @param[in] signal - received signal - * @return decoded irda message + * @return decoded IRDA message */ -const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal); +const IrdaMessage* irda_worker_get_decoded_signal(const IrdaWorkerSignal* signal); -/** Enable blinking on receiving any signal on IR port. +/** Set current decoded signal for IrdaWorker instance * - * @param[in] instance - instance of IrdaWorker - * @param[in] enable - true if you want to enable blinking - * false otherwise + * @param[out] instance - IrdaWorker instance + * @param[in] message - decoded signal */ -void irda_worker_enable_blink_on_receiving(IrdaWorker* instance, bool enable); +void irda_worker_set_decoded_signal(IrdaWorker* instance, const IrdaMessage* message); + +/** Set current raw signal for IrdaWorker instance + * + * @param[out] instance - IrdaWorker instance + * @param[in] timings - array of raw timings + * @param[in] timings_cnt - size of array of raw timings + */ +void irda_worker_set_raw_signal(IrdaWorker* instance, const uint32_t* timings, size_t timings_cnt); #ifdef __cplusplus }