[FL-1652, FL-1554] IRDA: Continuous transmitting (#636)

* [FL-1652] IRDA: Continuous transmitting
* continuous encoding and sending signals by pressing button on menu
* fast buttons scrolling in remote menu
* bruteforce: stop reading file if progress == 100%
* IRDA: .hpp -> .h
* [FL-1554] IRDA: xTaskNotify -> osEventsFlagSet
* IRDA: some stability fixes
* Irda: minor cleanup, api-hal to furi-hal rename.

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Albert Kharisov 2021-08-11 20:51:06 +03:00 committed by GitHub
parent 8696355556
commit 5ed9bdbc37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 804 additions and 218 deletions

View File

@ -1,6 +1,7 @@
#include "button_menu.h" #include "button_menu.h"
#include "gui/canvas.h" #include "gui/canvas.h"
#include "gui/elements.h" #include "gui/elements.h"
#include "input/input.h"
#include <m-array.h> #include <m-array.h>
#include <furi.h> #include <furi.h>
#include <stdint.h> #include <stdint.h>
@ -23,6 +24,7 @@ ARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST);
struct ButtonMenu { struct ButtonMenu {
View* view; View* view;
bool freeze_input;
}; };
typedef struct { 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); furi_assert(button_menu);
ButtonMenuItem* item = NULL; ButtonMenuItem* item = NULL;
@ -168,11 +170,22 @@ static void button_menu_process_ok(ButtonMenu* button_menu) {
if(model->position < (ButtonMenuItemArray_size(model->items))) { if(model->position < (ButtonMenuItemArray_size(model->items))) {
item = ButtonMenuItemArray_get(model->items, model->position); item = ButtonMenuItemArray_get(model->items, model->position);
} }
return true; return false;
}); });
if(item->type == ButtonMenuItemTypeControl) {
if(type == InputTypeShort) {
if(item && item->callback) { if(item && item->callback) {
item->callback(item->callback_context, item->index); 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; ButtonMenu* button_menu = context;
bool consumed = false; 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) { switch(event->key) {
case InputKeyUp: case InputKeyUp:
consumed = true; consumed = true;
@ -192,10 +217,6 @@ static bool button_menu_view_input_callback(InputEvent* event, void* context) {
consumed = true; consumed = true;
button_menu_process_down(button_menu); button_menu_process_down(button_menu);
break; break;
case InputKeyOk:
consumed = true;
button_menu_process_ok(button_menu);
break;
default: default:
break; break;
} }
@ -272,6 +293,7 @@ ButtonMenu* button_menu_alloc(void) {
return true; return true;
}); });
button_menu->freeze_input = false;
return button_menu; return button_menu;
} }

View File

@ -11,7 +11,7 @@ typedef struct ButtonMenu ButtonMenu;
typedef struct ButtonMenuItem ButtonMenuItem; typedef struct ButtonMenuItem ButtonMenuItem;
/* Callback for any button menu actions */ /* 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. */ /* Type of button. Difference in drawing buttons. */
typedef enum { typedef enum {

View File

@ -19,7 +19,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
Cli* cli = (Cli*)context; Cli* cli = (Cli*)context;
if(irda_worker_signal_is_decoded(received_signal)) { 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_cnt = sniprintf(
buf, buf,
sizeof(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(); IrdaWorker* worker = irda_worker_alloc();
irda_worker_set_context(worker, cli); irda_worker_rx_start(worker);
irda_worker_start(worker); irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
irda_worker_set_received_signal_callback(worker, signal_received_callback);
printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) { while(!cli_cmd_interrupt_received(cli)) {
delay(50); delay(50);
} }
irda_worker_stop(worker); irda_worker_rx_stop(worker);
irda_worker_free(worker); irda_worker_free(worker);
} }

View File

@ -1,5 +1,5 @@
#include "irda-app-brute-force.hpp" #include "irda-app-brute-force.h"
#include "irda/irda-app-file-parser.hpp" #include "irda/irda-app-file-parser.h"
#include "m-string.h" #include "m-string.h"
#include <file-worker-cpp.h> #include <file-worker-cpp.h>
#include <memory> #include <memory>
@ -47,7 +47,6 @@ void IrdaAppBruteForce::stop_bruteforce() {
} }
} }
// TODO: [FL-1418] replace with timer-chained consequence of messages.
bool IrdaAppBruteForce::send_next_bruteforce(void) { bool IrdaAppBruteForce::send_next_bruteforce(void) {
furi_assert(current_record.size()); furi_assert(current_record.size());
furi_assert(file_parser); furi_assert(file_parser);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "furi/check.h" #include "furi/check.h"
#include <unordered_map> #include <unordered_map>
#include "irda-app-file-parser.hpp" #include "irda-app-file-parser.h"
#include <memory> #include <memory>
class IrdaAppBruteForce { class IrdaAppBruteForce {
@ -28,7 +28,9 @@ public:
bool start_bruteforce(int index, int& record_amount); bool start_bruteforce(int index, int& record_amount);
void add_record(int index, const char* name); void add_record(int index, const char* name);
IrdaAppBruteForce(const char* filename) : universal_db_filename (filename) {} IrdaAppBruteForce(const char* filename)
~IrdaAppBruteForce() {} : universal_db_filename(filename) {
}
~IrdaAppBruteForce() {
}
}; };

View File

@ -9,6 +9,8 @@ public:
Exit, Exit,
Back, Back,
MenuSelected, MenuSelected,
MenuSelectedPress,
MenuSelectedRelease,
DialogExSelected, DialogExSelected,
NextScene, NextScene,
IrdaMessageReceived, IrdaMessageReceived,
@ -24,4 +26,3 @@ public:
Type type; Type type;
}; };

View File

@ -1,6 +1,6 @@
#include "irda-app-file-parser.hpp" #include "irda-app-file-parser.h"
#include "furi/check.h" #include "furi/check.h"
#include "irda-app-remote-manager.hpp" #include "irda-app-remote-manager.h"
#include "irda-app-signal.h" #include "irda-app-signal.h"
#include "m-string.h" #include "m-string.h"
#include <text-store.h> #include <text-store.h>

View File

@ -26,8 +26,16 @@ public:
std::string make_name(const std::string& full_name) const; std::string make_name(const std::string& full_name) const;
private: private:
size_t stringify_message(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); size_t stringify_message(
size_t stringify_raw_signal(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); 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<IrdaFileSignal> parse_signal(const std::string& str) const; std::unique_ptr<IrdaFileSignal> parse_signal(const std::string& str) const;
std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const; std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const;
std::string make_full_name(const std::string& name) const; std::string make_full_name(const std::string& name) const;
@ -41,4 +49,3 @@ private:
char file_buf[128]; char file_buf[128];
size_t file_buf_cnt = 0; size_t file_buf_cnt = 0;
}; };

View File

@ -1,4 +1,4 @@
#include "irda-app-remote-manager.hpp" #include "irda-app-remote-manager.h"
#include <storage/storage.h> #include <storage/storage.h>
#include "furi.h" #include "furi.h"
#include "furi/check.h" #include "furi/check.h"
@ -8,7 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <utility> #include <utility>
#include "irda-app-file-parser.hpp" #include "irda-app-file-parser.h"
static const std::string default_remote_name = "remote"; static const std::string default_remote_name = "remote";

View File

@ -12,21 +12,27 @@ class IrdaAppRemoteButton {
friend class IrdaAppRemoteManager; friend class IrdaAppRemoteManager;
std::string name; std::string name;
IrdaAppSignal signal; IrdaAppSignal signal;
public: public:
IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal) IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal)
: name(name), signal (signal) {} : name(name)
~IrdaAppRemoteButton() {} , signal(signal) {
}
~IrdaAppRemoteButton() {
}
}; };
class IrdaAppRemote { class IrdaAppRemote {
friend class IrdaAppRemoteManager; friend class IrdaAppRemoteManager;
std::vector<IrdaAppRemoteButton> buttons; std::vector<IrdaAppRemoteButton> buttons;
std::string name; 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; name = new_name;
buttons.clear(); buttons.clear();
return *this; return *this;
@ -61,4 +67,3 @@ public:
bool store(); bool store();
bool load(const std::string& name); bool load(const std::string& name);
}; };

View File

@ -1,7 +1,7 @@
#include "furi.h" #include "furi.h"
#include "gui/modules/button_panel.h" #include "gui/modules/button_panel.h"
#include "irda-app.hpp" #include "irda-app.h"
#include "irda/irda-app-event.hpp" #include "irda/irda-app-event.h"
#include <callback-connector.h> #include <callback-connector.h>
IrdaAppViewManager::IrdaAppViewManager() { IrdaAppViewManager::IrdaAppViewManager() {
@ -112,8 +112,14 @@ void IrdaAppViewManager::receive_event(IrdaAppEvent* event) {
} }
void IrdaAppViewManager::send_event(IrdaAppEvent* event) { void IrdaAppViewManager::send_event(IrdaAppEvent* event) {
osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0); uint32_t timeout = 0;
furi_check(result == osOK); /* 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) { uint32_t IrdaAppViewManager::previous_view_callback(void* context) {

View File

@ -6,7 +6,7 @@
#include <gui/modules/dialog_ex.h> #include <gui/modules/dialog_ex.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
#include <gui/modules/popup.h> #include <gui/modules/popup.h>
#include "irda-app.hpp" #include "irda-app.h"
#include "view/irda-app-brut-view.h" #include "view/irda-app-brut-view.h"
#include "gui/modules/button_panel.h" #include "gui/modules/button_panel.h"
@ -57,4 +57,3 @@ private:
void add_view(ViewType view_type, View* view); void add_view(ViewType view_type, View* view);
}; };

View File

@ -1,5 +1,5 @@
#include "irda-app.hpp" #include "irda-app.h"
#include "irda/irda-app-file-parser.hpp" #include "irda/irda-app-file-parser.h"
#include <irda_worker.h> #include <irda_worker.h>
#include <furi.h> #include <furi.h>
#include <gui/gui.h> #include <gui/gui.h>
@ -222,22 +222,33 @@ void IrdaApp::notify_click() {
notification_message_block(notification, &sequence); notification_message_block(notification, &sequence);
} }
void IrdaApp::notify_click_and_blink() { void IrdaApp::notify_click_and_green_blink() {
static const NotificationSequence sequence = { static const NotificationSequence sequence = {
&message_click, &message_click,
&message_delay_1, &message_delay_1,
&message_sound_off, &message_sound_off,
&message_red_0,
&message_green_255, &message_green_255,
&message_blue_0,
&message_delay_10, &message_delay_10,
&message_green_0, &message_green_0,
&message_do_not_reset,
NULL, NULL,
}; };
notification_message_block(notification, &sequence); 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() { void IrdaApp::notify_double_vibro() {
notification_message(notification, &sequence_double_vibro); notification_message(notification, &sequence_double_vibro);
} }

View File

@ -2,17 +2,16 @@
#include <map> #include <map>
#include <irda.h> #include <irda.h>
#include <furi.h> #include <furi.h>
#include "scene/irda-app-scene.hpp" #include "scene/irda-app-scene.h"
#include "irda-app-event.hpp" #include "irda-app-event.h"
#include "scene/irda-app-scene.hpp" #include "scene/irda-app-scene.h"
#include "irda-app-view-manager.hpp" #include "irda-app-view-manager.h"
#include "irda-app-remote-manager.hpp" #include "irda-app-remote-manager.h"
#include <forward_list> #include <forward_list>
#include <stdint.h> #include <stdint.h>
#include <notification/notification-messages.h> #include <notification/notification-messages.h>
#include <irda_worker.h> #include <irda_worker.h>
class IrdaApp { class IrdaApp {
public: public:
enum class EditElement : uint8_t { enum class EditElement : uint8_t {
@ -83,7 +82,8 @@ public:
void notify_green_on(); void notify_green_on();
void notify_green_off(); void notify_green_off();
void notify_click(); 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 text_input_callback(void* context);
static void popup_callback(void* context); static void popup_callback(void* context);
@ -95,9 +95,9 @@ public:
~IrdaApp() { ~IrdaApp() {
irda_worker_free(irda_worker); irda_worker_free(irda_worker);
furi_record_close("notification"); furi_record_close("notification");
for (auto &it : scenes) for(auto& it : scenes) delete it.second;
delete it.second;
} }
private: private:
static const uint8_t text_store_size = 128; static const uint8_t text_store_size = 128;
static const uint8_t text_store_max = 2; static const uint8_t text_store_max = 2;
@ -120,7 +120,7 @@ private:
{Scene::Start, new IrdaAppSceneStart()}, {Scene::Start, new IrdaAppSceneStart()},
{Scene::Universal, new IrdaAppSceneUniversal()}, {Scene::Universal, new IrdaAppSceneUniversal()},
{Scene::UniversalTV, new IrdaAppSceneUniversalTV()}, {Scene::UniversalTV, new IrdaAppSceneUniversalTV()},
// {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()}, // {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()},
{Scene::Learn, new IrdaAppSceneLearn()}, {Scene::Learn, new IrdaAppSceneLearn()},
{Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()},
{Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()},

View File

@ -1,4 +1,4 @@
#include "irda-app.hpp" #include "irda-app.h"
extern "C" int32_t irda_app(void* p) { extern "C" int32_t irda_app(void* p) {
IrdaApp* app = new IrdaApp(); IrdaApp* app = new IrdaApp();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) { void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,6 +1,6 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "irda.h" #include "irda.h"
#include "irda/scene/irda-app-scene.hpp" #include "irda/scene/irda-app-scene.h"
#include <string> #include <string>
static void dialog_result_callback(DialogExResult result, void* context) { static void dialog_result_callback(DialogExResult result, void* context) {

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "gui/modules/submenu.h" #include "gui/modules/submenu.h"
static void submenu_callback(void* context, uint32_t index) { static void submenu_callback(void* context, uint32_t index) {

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) { void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "gui/modules/submenu.h" #include "gui/modules/submenu.h"
typedef enum { typedef enum {

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "gui/modules/text_input.h" #include "gui/modules/text_input.h"
void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {

View File

@ -1,6 +1,6 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "irda.h" #include "irda.h"
#include "../irda-app-file-parser.hpp" #include "../irda-app-file-parser.h"
#include <memory> #include <memory>
static void dialog_result_callback(DialogExResult result, void* context) { 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 IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
bool consumed = false; 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) { if(event->type == IrdaAppEvent::Type::DialogExSelected) {
switch(event->payload.dialog_ex_result) { switch(event->payload.dialog_ex_result) {

View File

@ -1,5 +1,5 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "../irda-app-event.hpp" #include "../irda-app-event.h"
#include <irda_worker.h> #include <irda_worker.h>
static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { 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<IrdaApp*>(context); IrdaApp* app = static_cast<IrdaApp*>(context);
if(irda_worker_signal_is_decoded(received_signal)) { 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); app->set_received_signal(signal);
} else { } else {
const uint32_t* timings; const uint32_t* timings;
@ -19,7 +19,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
app->set_received_signal(signal); 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; IrdaAppEvent event;
event.type = IrdaAppEvent::Type::IrdaMessageReceived; event.type = IrdaAppEvent::Type::IrdaMessageReceived;
auto view_manager = app->get_view_manager(); auto view_manager = app->get_view_manager();
@ -31,9 +31,8 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
auto popup = view_manager->get_popup(); auto popup = view_manager->get_popup();
auto worker = app->get_irda_worker(); auto worker = app->get_irda_worker();
irda_worker_set_context(worker, app); irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, app);
irda_worker_set_received_signal_callback(worker, signal_received_callback); irda_worker_rx_start(worker);
irda_worker_start(worker);
popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31); popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31);
popup_set_text( popup_set_text(
@ -58,11 +57,9 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) {
case IrdaAppEvent::Type::IrdaMessageReceived: case IrdaAppEvent::Type::IrdaMessageReceived:
app->notify_success(); app->notify_success();
app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess);
irda_worker_stop(app->get_irda_worker());
break; break;
case IrdaAppEvent::Type::Back: case IrdaAppEvent::Type::Back:
consumed = true; consumed = true;
irda_worker_stop(app->get_irda_worker());
app->switch_to_previous_scene(); app->switch_to_previous_scene();
break; break;
default: default:
@ -73,4 +70,5 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) {
} }
void IrdaAppSceneLearn::on_exit(IrdaApp* app) { void IrdaAppSceneLearn::on_exit(IrdaApp* app) {
irda_worker_rx_stop(app->get_irda_worker());
} }

View File

@ -1,5 +1,5 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "irda/irda-app-event.hpp" #include "irda/irda-app-event.h"
void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) {
IrdaAppFileParser file_parser; IrdaAppFileParser file_parser;

View File

@ -1,5 +1,7 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "gui/modules/button_menu.h" #include "gui/modules/button_menu.h"
#include "input/input.h"
#include "irda_worker.h"
typedef enum { typedef enum {
ButtonIndexPlus = -2, ButtonIndexPlus = -2,
@ -7,22 +9,41 @@ typedef enum {
ButtonIndexNA = 0, ButtonIndexNA = 0,
} ButtonIndex; } 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<IrdaApp*>(context); IrdaApp* app = static_cast<IrdaApp*>(context);
IrdaAppEvent event; IrdaAppEvent event;
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; event.type = IrdaAppEvent::Type::MenuSelected;
} else {
furi_assert(0);
}
event.payload.menu_index = index; event.payload.menu_index = index;
app->get_view_manager()->send_event(&event); app->get_view_manager()->send_event(&event);
} }
static void irda_app_message_sent_callback(void* context) {
IrdaApp* app = static_cast<IrdaApp*>(context);
app->notify_blink_green();
}
void IrdaAppSceneRemote::on_enter(IrdaApp* app) { void IrdaAppSceneRemote::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();
ButtonMenu* button_menu = view_manager->get_button_menu(); ButtonMenu* button_menu = view_manager->get_button_menu();
auto remote_manager = app->get_remote_manager(); auto remote_manager = app->get_remote_manager();
int i = 0; 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(); buttons_names = remote_manager->get_button_list();
i = 0; i = 0;
@ -48,24 +69,49 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) {
bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) {
bool consumed = true; 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) { switch(event->payload.menu_index) {
case ButtonIndexPlus: case ButtonIndexPlus:
furi_assert(event->type == IrdaAppEvent::Type::MenuSelected);
app->notify_click(); app->notify_click();
buttonmenu_item_selected = event->payload.menu_index; buttonmenu_item_selected = event->payload.menu_index;
app->set_learn_new_remote(false); app->set_learn_new_remote(false);
app->switch_to_next_scene(IrdaApp::Scene::Learn); app->switch_to_next_scene(IrdaApp::Scene::Learn);
break; break;
case ButtonIndexEdit: case ButtonIndexEdit:
furi_assert(event->type == IrdaAppEvent::Type::MenuSelected);
app->notify_click(); app->notify_click();
buttonmenu_item_selected = event->payload.menu_index; buttonmenu_item_selected = event->payload.menu_index;
app->switch_to_next_scene(IrdaApp::Scene::Edit); app->switch_to_next_scene(IrdaApp::Scene::Edit);
break; break;
default: default:
app->notify_click_and_blink(); furi_assert(event->type != IrdaAppEvent::Type::MenuSelected);
auto remote_manager = app->get_remote_manager(); bool pressed = (event->type == IrdaAppEvent::Type::MenuSelectedPress);
auto signal = remote_manager->get_button_data(event->payload.menu_index);
signal.transmit(); 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; break;
} }
} else if(event->type == IrdaAppEvent::Type::Back) { } 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) { 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(); IrdaAppViewManager* view_manager = app->get_view_manager();
ButtonMenu* button_menu = view_manager->get_button_menu(); ButtonMenu* button_menu = view_manager->get_button_menu();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
typedef enum { typedef enum {
SubmenuIndexUniversalLibrary, SubmenuIndexUniversalLibrary,

View File

@ -1,12 +1,12 @@
#include "../irda-app.hpp" #include "../irda-app.h"
#include "assets_icons.h" #include "assets_icons.h"
#include "gui/modules/button_menu.h" #include "gui/modules/button_menu.h"
#include "gui/modules/button_panel.h" #include "gui/modules/button_panel.h"
#include "../view/irda-app-brut-view.h" #include "../view/irda-app-brut-view.h"
#include "gui/view.h" #include "gui/view.h"
#include "irda/irda-app-event.hpp" #include "irda/irda-app-event.h"
#include "irda/irda-app-view-manager.hpp" #include "irda/irda-app-view-manager.h"
#include "irda/scene/irda-app-scene.hpp" #include "irda/scene/irda-app-scene.h"
void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) { void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) {
IrdaApp* app = static_cast<IrdaApp*>(context); IrdaApp* app = static_cast<IrdaApp*>(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); button_panel_set_popup_input_callback(button_panel, irda_popup_brut_input_callback, app);
} }
void IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) { bool IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) {
popup_brut_increase_progress(app->get_view_manager()->get_popup_brut()); bool result = popup_brut_increase_progress(app->get_view_manager()->get_popup_brut());
auto button_panel = app->get_view_manager()->get_button_panel(); auto button_panel = app->get_view_manager()->get_button_panel();
with_view_model_cpp(button_panel_get_view(button_panel), void*, model, { return true; }); with_view_model_cpp(button_panel_get_view(button_panel), void*, model, { return true; });
return result;
} }
bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { 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(); auto view_manager = app->get_view_manager();
IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick};
view_manager->send_event(&tick_event); view_manager->send_event(&tick_event);
if(brute_force.send_next_bruteforce()) { bool result = brute_force.send_next_bruteforce();
progress_popup(app); if(result) {
} else { result = progress_popup(app);
}
if(!result) {
brute_force.stop_bruteforce(); brute_force.stop_bruteforce();
brute_force_started = false; brute_force_started = false;
remove_popup(app); remove_popup(app);

View File

@ -1,5 +1,5 @@
#include "irda/scene/irda-app-scene.hpp" #include "irda/scene/irda-app-scene.h"
#include "irda/irda-app.hpp" #include "irda/irda-app.h"
void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager(); IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,4 +1,4 @@
#include "../irda-app.hpp" #include "../irda-app.h"
typedef enum { typedef enum {
SubmenuIndexUniversalTV, SubmenuIndexUniversalTV,

View File

@ -1,11 +1,10 @@
#pragma once #pragma once
#include "../irda-app-event.hpp" #include "../irda-app-event.h"
#include <furi-hal-irda.h> #include <furi-hal-irda.h>
#include "irda.h" #include "irda.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include "../irda-app-brute-force.hpp" #include "../irda-app-brute-force.h"
class IrdaApp; class IrdaApp;
@ -24,6 +23,7 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
uint32_t submenu_item_selected = 0; uint32_t submenu_item_selected = 0;
}; };
@ -33,6 +33,7 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
uint32_t submenu_item_selected = 0; uint32_t submenu_item_selected = 0;
}; };
@ -70,9 +71,11 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
std::vector<std::string> buttons_names; std::vector<std::string> buttons_names;
uint32_t buttonmenu_item_selected = 0; uint32_t buttonmenu_item_selected = 0;
bool button_pressed = false;
}; };
class IrdaAppSceneRemoteList : public IrdaAppScene { class IrdaAppSceneRemoteList : public IrdaAppScene {
@ -80,6 +83,7 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
uint32_t submenu_item_selected = 0; uint32_t submenu_item_selected = 0;
std::vector<std::string> remote_names; std::vector<std::string> remote_names;
@ -90,6 +94,7 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
uint32_t submenu_item_selected = 0; uint32_t submenu_item_selected = 0;
}; };
@ -99,6 +104,7 @@ public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
private: private:
std::vector<std::string> buttons_names; std::vector<std::string> buttons_names;
}; };
@ -133,16 +139,20 @@ public:
class IrdaAppSceneUniversalCommon : public IrdaAppScene { class IrdaAppSceneUniversalCommon : public IrdaAppScene {
bool brute_force_started = false; bool brute_force_started = false;
protected: protected:
bool on_event(IrdaApp* app, IrdaAppEvent* event) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final; void on_exit(IrdaApp* app) final;
IrdaAppBruteForce brute_force; IrdaAppBruteForce brute_force;
void remove_popup(IrdaApp* app); void remove_popup(IrdaApp* app);
void show_popup(IrdaApp* app, int record_amount); 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); static void irda_app_item_callback(void* context, uint32_t index);
IrdaAppSceneUniversalCommon(const char* filename) : brute_force(filename) {} IrdaAppSceneUniversalCommon(const char* filename)
~IrdaAppSceneUniversalCommon() {} : brute_force(filename) {
}
~IrdaAppSceneUniversalCommon() {
}
}; };
class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon { class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon {
@ -151,13 +161,16 @@ public:
IrdaAppSceneUniversalTV() IrdaAppSceneUniversalTV()
: IrdaAppSceneUniversalCommon("/ext/irda/universal/tv.ir") { : IrdaAppSceneUniversalCommon("/ext/irda/universal/tv.ir") {
} }
~IrdaAppSceneUniversalTV() {} ~IrdaAppSceneUniversalTV() {
}
}; };
class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon { class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon {
public: public:
void on_enter(IrdaApp* app) final; void on_enter(IrdaApp* app) final;
IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/ext/irda/universal/audio.ir") {} IrdaAppSceneUniversalAudio()
~IrdaAppSceneUniversalAudio() {} : IrdaAppSceneUniversalCommon("/ext/irda/universal/audio.ir") {
}
~IrdaAppSceneUniversalAudio() {
}
}; };

View File

@ -15,13 +15,15 @@ struct IrdaAppPopupBrut {
char percents_string_storage[8]; 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); furi_assert(popup_brut);
if(popup_brut->progress < popup_brut->progress_max) if(popup_brut->progress < popup_brut->progress_max)
++popup_brut->progress; ++popup_brut->progress;
else else
furi_assert(0); furi_assert(0);
return popup_brut->progress < popup_brut->progress_max;
} }
void popup_brut_draw_callback(Canvas* canvas, void* context) { void popup_brut_draw_callback(Canvas* canvas, void* context) {

View File

@ -7,7 +7,7 @@ extern "C" {
typedef struct IrdaAppPopupBrut IrdaAppPopupBrut; typedef struct IrdaAppPopupBrut IrdaAppPopupBrut;
void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut); bool popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut);
IrdaAppPopupBrut* popup_brut_alloc(); IrdaAppPopupBrut* popup_brut_alloc();
void popup_brut_free(IrdaAppPopupBrut* popup_brut); void popup_brut_free(IrdaAppPopupBrut* popup_brut);
void popup_brut_draw_callback(Canvas* canvas, void* model); void popup_brut_draw_callback(Canvas* canvas, void* model);

View File

@ -58,7 +58,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
IrdaMonitor* irda_monitor = context; IrdaMonitor* irda_monitor = context;
if(irda_worker_signal_is_decoded(received_signal)) { 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( snprintf(
irda_monitor->display_text, irda_monitor->display_text,
sizeof(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); gui_add_view_port(gui, irda_monitor->view_port, GuiLayerFullscreen);
irda_monitor->worker = irda_worker_alloc(); irda_monitor->worker = irda_worker_alloc();
irda_worker_set_context(irda_monitor->worker, irda_monitor); irda_worker_rx_start(irda_monitor->worker);
irda_worker_start(irda_monitor->worker); irda_worker_rx_set_received_signal_callback(
irda_worker_set_received_signal_callback(irda_monitor->worker, signal_received_callback); irda_monitor->worker, signal_received_callback, irda_monitor);
irda_worker_enable_blink_on_receiving(irda_monitor->worker, true); irda_worker_rx_enable_blink_on_receiving(irda_monitor->worker, true);
while(1) { while(1) {
InputEvent event; 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); irda_worker_free(irda_monitor->worker);
osMessageQueueDelete(irda_monitor->event_queue); osMessageQueueDelete(irda_monitor->event_queue);
view_port_enabled_set(irda_monitor->view_port, false); view_port_enabled_set(irda_monitor->view_port, false);

View File

@ -40,8 +40,10 @@ typedef struct{
typedef struct { typedef struct {
float cycle_duration; float cycle_duration;
FuriHalIrdaTxGetDataCallback data_callback; FuriHalIrdaTxGetDataISRCallback data_callback;
FuriHalIrdaTxSignalSentISRCallback signal_sent_callback;
void* data_context; void* data_context;
void* signal_sent_context;
IrdaTxBuf buffer[2]; IrdaTxBuf buffer[2];
osSemaphoreId_t stop_semaphore; osSemaphoreId_t stop_semaphore;
} IrdaTimTx; } IrdaTimTx;
@ -175,8 +177,10 @@ void furi_hal_irda_async_rx_stop(void) {
furi_hal_irda_state = IrdaStateIdle; furi_hal_irda_state = IrdaStateIdle;
} }
void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_ms) { void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_us) {
LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); 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_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3);
LL_TIM_EnableIT_CC3(TIM2); 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 */ /* if it's not end of the packet - continue receiving */
furi_hal_irda_tx_dma_set_buffer(next_buf_num); 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(); 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); furi_assert(furi_hal_irda_state == IrdaStateIdle);
irda_tim_tx.data_callback = callback; irda_tim_tx.data_callback = callback;
irda_tim_tx.data_context = context; 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;
}

View File

@ -14,7 +14,15 @@ typedef enum {
FuriHalIrdaTxGetDataStateLastDone, /* New data obtained, and this is end of package and no more data available */ FuriHalIrdaTxGetDataStateLastDone, /* New data obtained, and this is end of package and no more data available */
} FuriHalIrdaTxGetDataState; } 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. * 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); 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()'. * 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. * 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] callback - callback to call when RX signal edge changing occurs
* @param[in] ctx - context for callback * @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. * 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] callback - callback for silence timeout
* @param[in] ctx - context to pass to callback * @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] callback - function to provide new data
* @param[in] context - context for callback * @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: * 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); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -34,6 +34,8 @@ typedef struct {
IrdaEncoders encoder; IrdaEncoders encoder;
uint8_t address_length; uint8_t address_length;
uint8_t command_length; uint8_t command_length;
uint32_t frequency;
float duty_cycle;
} IrdaProtocolImplementation; } IrdaProtocolImplementation;
struct IrdaEncoderHandler { struct IrdaEncoderHandler {
@ -58,6 +60,8 @@ static const IrdaProtocolImplementation irda_protocols[] = {
.free = irda_encoder_nec_free}, .free = irda_encoder_nec_free},
.address_length = 2, .address_length = 2,
.command_length = 2, .command_length = 2,
.frequency = IRDA_COMMON_CARRIER_FREQUENCY,
.duty_cycle = IRDA_COMMON_DUTY_CYCLE,
}, },
// #1 - have to be after NEC // #1 - have to be after NEC
{ .protocol = IrdaProtocolNECext, { .protocol = IrdaProtocolNECext,
@ -74,6 +78,8 @@ static const IrdaProtocolImplementation irda_protocols[] = {
.free = irda_encoder_nec_free}, .free = irda_encoder_nec_free},
.address_length = 4, .address_length = 4,
.command_length = 2, .command_length = 2,
.frequency = IRDA_COMMON_CARRIER_FREQUENCY,
.duty_cycle = IRDA_COMMON_DUTY_CYCLE,
}, },
// #2 // #2
{ .protocol = IrdaProtocolSamsung32, { .protocol = IrdaProtocolSamsung32,
@ -90,6 +96,8 @@ static const IrdaProtocolImplementation irda_protocols[] = {
.free = irda_encoder_samsung32_free}, .free = irda_encoder_samsung32_free},
.address_length = 2, .address_length = 2,
.command_length = 2, .command_length = 2,
.frequency = IRDA_COMMON_CARRIER_FREQUENCY,
.duty_cycle = IRDA_COMMON_DUTY_CYCLE,
}, },
// #3 // #3
{ .protocol = IrdaProtocolRC6, { .protocol = IrdaProtocolRC6,
@ -106,6 +114,8 @@ static const IrdaProtocolImplementation irda_protocols[] = {
.free = irda_encoder_rc6_free}, .free = irda_encoder_rc6_free},
.address_length = 2, .address_length = 2,
.command_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)) if (!strcmp(irda_protocols[i].name, protocol_name))
return i; return i;
} }
furi_assert(0);
return IrdaProtocolUnknown; return IrdaProtocolUnknown;
} }
const char* irda_get_protocol_name(IrdaProtocol protocol) { const char* irda_get_protocol_name(IrdaProtocol protocol) {
furi_assert(irda_is_protocol_valid(protocol));
if (irda_is_protocol_valid(protocol)) if (irda_is_protocol_valid(protocol))
return irda_protocols[protocol].name; return irda_protocols[protocol].name;
else else
@ -233,6 +245,7 @@ const char* irda_get_protocol_name(IrdaProtocol protocol) {
} }
uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) {
furi_assert(irda_is_protocol_valid(protocol));
if (irda_is_protocol_valid(protocol)) if (irda_is_protocol_valid(protocol))
return irda_protocols[protocol].address_length; return irda_protocols[protocol].address_length;
else else
@ -240,9 +253,26 @@ uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) {
} }
uint8_t irda_get_protocol_command_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)) if (irda_is_protocol_valid(protocol))
return irda_protocols[protocol].command_length; return irda_protocols[protocol].command_length;
else else
return 0; 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;
}

View File

@ -10,6 +10,12 @@ extern "C" {
#define IRDA_COMMON_CARRIER_FREQUENCY 38000 #define IRDA_COMMON_CARRIER_FREQUENCY 38000
#define IRDA_COMMON_DUTY_CYCLE 0.33 #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 IrdaDecoderHandler IrdaDecoderHandler;
typedef struct IrdaEncoderHandler IrdaEncoderHandler; 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); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -137,7 +137,8 @@ extern const IrdaCommonProtocolSpec protocol_samsung32;
#define IRDA_RC6_BIT 444 // half of time-quant for 1 bit #define IRDA_RC6_BIT 444 // half of time-quant for 1 bit
#define IRDA_RC6_PREAMBLE_TOLERANCE 0.07 // percents #define IRDA_RC6_PREAMBLE_TOLERANCE 0.07 // percents
#define IRDA_RC6_BIT_TOLERANCE 120 // us #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_alloc(void);
void irda_decoder_rc6_reset(void* decoder); void irda_decoder_rc6_reset(void* decoder);

View File

@ -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)) { if (irda_tx_raw_add_silence && (irda_tx_raw_timings_index == 0)) {
irda_tx_raw_add_silence = false; irda_tx_raw_add_silence = false;
*level = false; *level = false;
*duration = 180000; // 180 ms delay between raw packets *duration = IRDA_RAW_TX_TIMING_DELAY_US;
} else { } else {
*level = irda_tx_raw_start_from_mark ^ (irda_tx_raw_timings_index % 2); *level = irda_tx_raw_start_from_mark ^ (irda_tx_raw_timings_index % 2);
*duration = timings[irda_tx_raw_timings_index++]; *duration = timings[irda_tx_raw_timings_index++];

View File

@ -1,20 +1,45 @@
#include "furi/check.h"
#include "furi/common_defines.h"
#include "sys/_stdint.h"
#include "irda_worker.h" #include "irda_worker.h"
#include <irda.h> #include <irda.h>
#include <furi-hal-irda.h> #include <furi-hal-irda.h>
#include <limits.h> #include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stream_buffer.h>
#include <furi.h> #include <furi.h>
#include <notification/notification-messages.h> #include <notification/notification-messages.h>
#include <stream_buffer.h>
#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_RECEIVED 0x01
#define IRDA_WORKER_RX_TIMEOUT_RECEIVED 0x02 #define IRDA_WORKER_RX_TIMEOUT_RECEIVED 0x02
#define IRDA_WORKER_OVERRUN 0x04 #define IRDA_WORKER_OVERRUN 0x04
#define IRDA_WORKER_EXIT 0x08 #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; bool decoded;
size_t timings_cnt; size_t timings_cnt;
union { union {
@ -25,37 +50,67 @@ struct IrdaWorkerSignal {
struct IrdaWorker { struct IrdaWorker {
FuriThread* thread; FuriThread* thread;
IrdaDecoderHandler* irda_decoder;
StreamBufferHandle_t stream; StreamBufferHandle_t stream;
osEventFlagsId_t events;
TaskHandle_t worker_handle;
IrdaWorkerSignal signal; IrdaWorkerSignal signal;
IrdaWorkerState state;
IrdaWorkerReceivedSignalCallback received_signal_callback; IrdaEncoderHandler* irda_encoder;
void* context; IrdaDecoderHandler* irda_decoder;
bool blink_enable;
bool overrun;
NotificationApp* notification; 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) { static void irda_worker_rx_timeout_callback(void* context) {
IrdaWorker* instance = context; IrdaWorker* instance = context;
BaseType_t xHigherPriorityTaskWoken = pdFALSE; uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_RX_TIMEOUT_RECEIVED);
xTaskNotifyFromISR(instance->worker_handle, IRDA_WORKER_RX_TIMEOUT_RECEIVED, eSetBits, &xHigherPriorityTaskWoken); furi_check(flags_set & IRDA_WORKER_RX_TIMEOUT_RECEIVED);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) { static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) {
IrdaWorker* instance = context; IrdaWorker* instance = context;
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
furi_assert(duration != 0);
LevelDuration level_duration = level_duration_make(level, duration); LevelDuration level_duration = level_duration_make(level, duration);
size_t ret = size_t ret =
xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken);
uint32_t notify_value = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN; uint32_t events = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN;
xTaskNotifyFromISR(instance->worker_handle, notify_value, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); 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) { static void irda_worker_process_timeout(IrdaWorker* instance) {
@ -63,8 +118,8 @@ static void irda_worker_process_timeout(IrdaWorker* instance) {
return; return;
instance->signal.decoded = false; instance->signal.decoded = false;
if (instance->received_signal_callback) if (instance->rx.received_signal_callback)
instance->received_signal_callback(instance->context, &instance->signal); 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) { 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.data.message = *message_decoded;
instance->signal.timings_cnt = 0; instance->signal.timings_cnt = 0;
instance->signal.decoded = true; instance->signal.decoded = true;
if (instance->received_signal_callback) if (instance->rx.received_signal_callback)
instance->received_signal_callback(instance->context, &instance->signal); instance->rx.received_signal_callback(instance->rx.received_signal_context, &instance->signal);
} else { } else {
/* Skip first timing if it's starts from Space */ /* Skip first timing if it's starts from Space */
if ((instance->signal.timings_cnt == 0) && !level) { 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.data.timings[instance->signal.timings_cnt] = duration;
++instance->signal.timings_cnt; ++instance->signal.timings_cnt;
} else { } else {
xTaskNotify(instance->worker_handle, IRDA_WORKER_OVERRUN, eSetBits); uint32_t flags_set = osEventFlagsSet(instance->events, IRDA_WORKER_OVERRUN);
instance->overrun = true; furi_check(flags_set & IRDA_WORKER_OVERRUN);
instance->rx.overrun = true;
} }
} }
} }
static int32_t irda_worker_thread_callback(void* context) { static int32_t irda_worker_rx_thread(void* thread_context) {
IrdaWorker* instance = context; IrdaWorker* instance = thread_context;
uint32_t notify_value = 0; uint32_t events = 0;
LevelDuration level_duration; LevelDuration level_duration;
TickType_t last_blink_time = 0; TickType_t last_blink_time = 0;
while(1) { while(1) {
BaseType_t result; events = osEventFlagsWait(instance->events, IRDA_WORKER_ALL_RX_EVENTS, 0, osWaitForever);
result = xTaskNotifyWait(pdFALSE, ULONG_MAX, &notify_value, 1000); furi_check(events & IRDA_WORKER_ALL_RX_EVENTS); /* at least one caught */
if (result != pdPASS)
continue;
if (notify_value & IRDA_WORKER_RX_RECEIVED) { if (events & IRDA_WORKER_RX_RECEIVED) {
if (!instance->overrun && instance->blink_enable && ((xTaskGetTickCount() - last_blink_time) > 80)) { if (!instance->rx.overrun && instance->blink_enable && ((xTaskGetTickCount() - last_blink_time) > 80)) {
last_blink_time = xTaskGetTickCount(); last_blink_time = xTaskGetTickCount();
notification_message(instance->notification, &sequence_blink_blue_10); notification_message(instance->notification, &sequence_blink_blue_10);
} }
if (instance->signal.timings_cnt == 0) if (instance->signal.timings_cnt == 0)
notification_message(instance->notification, &sequence_display_on); notification_message(instance->notification, &sequence_display_on);
while (sizeof(LevelDuration) == xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 0)) { 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); bool level = level_duration_get_level(level_duration);
uint32_t duration = level_duration_get_duration(level_duration); uint32_t duration = level_duration_get_duration(level_duration);
irda_worker_process_timings(instance, duration, level); irda_worker_process_timings(instance, duration, level);
} }
} }
} }
if (notify_value & IRDA_WORKER_OVERRUN) { if (events & IRDA_WORKER_OVERRUN) {
printf("#"); printf("#");
irda_reset_decoder(instance->irda_decoder); irda_reset_decoder(instance->irda_decoder);
instance->signal.timings_cnt = 0; instance->signal.timings_cnt = 0;
if (instance->blink_enable) if (instance->blink_enable)
notification_message(instance->notification, &sequence_set_red_255); notification_message(instance->notification, &sequence_set_red_255);
} }
if (notify_value & IRDA_WORKER_RX_TIMEOUT_RECEIVED) { if (events & IRDA_WORKER_RX_TIMEOUT_RECEIVED) {
if (instance->overrun) { if (instance->rx.overrun) {
printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT); printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT);
instance->overrun = false; instance->rx.overrun = false;
if (instance->blink_enable) if (instance->blink_enable)
notification_message(instance->notification, &sequence_reset_red); notification_message(instance->notification, &sequence_reset_red);
} else { } else {
@ -136,16 +190,17 @@ static int32_t irda_worker_thread_callback(void* context) {
} }
instance->signal.timings_cnt = 0; instance->signal.timings_cnt = 0;
} }
if (notify_value & IRDA_WORKER_EXIT) if (events & IRDA_WORKER_EXIT)
break; break;
} }
return 0; 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); furi_assert(instance);
instance->received_signal_callback = callback; instance->rx.received_signal_callback = callback;
instance->rx.received_signal_context = context;
} }
IrdaWorker* irda_worker_alloc() { IrdaWorker* irda_worker_alloc() {
@ -155,60 +210,67 @@ IrdaWorker* irda_worker_alloc() {
furi_thread_set_name(instance->thread, "irda_worker"); furi_thread_set_name(instance->thread, "irda_worker");
furi_thread_set_stack_size(instance->thread, 2048); furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance); 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_decoder = irda_alloc_decoder();
instance->irda_encoder = irda_alloc_encoder();
instance->blink_enable = false; instance->blink_enable = false;
instance->notification = furi_record_open("notification"); instance->notification = furi_record_open("notification");
instance->state = IrdaWorkerStateIdle;
instance->events = osEventFlagsNew(NULL);
return instance; return instance;
} }
void irda_worker_free(IrdaWorker* instance) { void irda_worker_free(IrdaWorker* instance) {
furi_assert(instance); furi_assert(instance);
furi_assert(!instance->worker_handle); furi_assert(instance->state == IrdaWorkerStateIdle);
furi_record_close("notification"); furi_record_close("notification");
irda_free_decoder(instance->irda_decoder); irda_free_decoder(instance->irda_decoder);
irda_free_encoder(instance->irda_encoder);
vStreamBufferDelete(instance->stream); vStreamBufferDelete(instance->stream);
furi_thread_free(instance->thread); furi_thread_free(instance->thread);
osEventFlagsDelete(instance->events);
free(instance); free(instance);
} }
void irda_worker_set_context(IrdaWorker* instance, void* context) { void irda_worker_rx_start(IrdaWorker* instance) {
furi_assert(instance); furi_assert(instance);
instance->context = context; furi_assert(instance->state == IrdaWorkerStateIdle);
}
void irda_worker_start(IrdaWorker* instance) { xStreamBufferSetTriggerLevel(instance->stream, sizeof(LevelDuration));
furi_assert(instance);
furi_assert(!instance->worker_handle);
osEventFlagsClear(instance->events, IRDA_WORKER_ALL_EVENTS);
furi_thread_set_callback(instance->thread, irda_worker_rx_thread);
furi_thread_start(instance->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_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_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);
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_timeout_isr_callback(NULL, NULL);
furi_hal_irda_async_rx_set_capture_isr_callback(NULL, NULL); furi_hal_irda_async_rx_set_capture_isr_callback(NULL, NULL);
furi_hal_irda_async_rx_stop(); furi_hal_irda_async_rx_stop();
xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits); osEventFlagsSet(instance->events, IRDA_WORKER_EXIT);
instance->worker_handle = NULL;
furi_thread_join(instance->thread); 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) { 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; *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); furi_assert(signal);
return &signal->data.message; 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); furi_assert(instance);
instance->blink_enable = enable; 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;
}

View File

@ -7,11 +7,28 @@
extern "C" { extern "C" {
#endif #endif
#define MAX_TIMINGS_AMOUNT 512
/** Interface struct of irda worker */ /** Interface struct of irda worker */
typedef struct IrdaWorker IrdaWorker; typedef struct IrdaWorker IrdaWorker;
/** Interface struct of received signal */ /** Interface struct of received signal */
typedef struct IrdaWorkerSignal IrdaWorkerSignal; 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 */ /** Callback type to call by IrdaWorker thread when new signal is received */
typedef void (*IrdaWorkerReceivedSignalCallback)(void* context, IrdaWorkerSignal* received_signal); typedef void (*IrdaWorkerReceivedSignalCallback)(void* context, IrdaWorkerSignal* received_signal);
@ -27,31 +44,33 @@ IrdaWorker* irda_worker_alloc();
*/ */
void irda_worker_free(IrdaWorker* instance); 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. /** Start IrdaWorker thread, initialise furi-hal, prepare all work.
* *
* @param[in] instance - IrdaWorker instance * @param[in] instance - IrdaWorker instance
*/ */
void irda_worker_start(IrdaWorker* instance); void irda_worker_rx_start(IrdaWorker* instance);
/** Stop IrdaWorker thread, deinitialize furi-hal. /** Stop IrdaWorker thread, deinitialize furi-hal.
* *
* @param[in] instance - IrdaWorker instance * @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 /** 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); 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'. /** Acquire raw signal from interface struct 'IrdaWorkerSignal'.
* First, you have to ensure that signal is raw. * 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. * First, you have to ensure that signal is decoded.
* *
* @param[in] signal - received signal * @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[out] instance - IrdaWorker instance
* @param[in] enable - true if you want to enable blinking * @param[in] message - decoded signal
* false otherwise
*/ */
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 #ifdef __cplusplus
} }