Преглед изворни кода

[FL-1505] Add RAW format (#576)

* Add RAW format
* F5 stubs for build to pass
* Fix saving decoded signal error
* Irda: set ISR before starting timer, remove explicit NVIC configuration

Co-authored-by: あく <alleteam@gmail.com>
Albert Kharisov пре 4 година
родитељ
комит
13c5a8cb20
50 измењених фајлова са 1236 додато и 941 уклоњено
  1. 121 53
      applications/irda/cli/irda-cli.cpp
  2. 8 8
      applications/irda/irda-app-brute-force.cpp
  3. 1 2
      applications/irda/irda-app-brute-force.hpp
  4. 150 8
      applications/irda/irda-app-file-parser.cpp
  5. 15 6
      applications/irda/irda-app-file-parser.hpp
  6. 11 29
      applications/irda/irda-app-remote-manager.cpp
  7. 9 6
      applications/irda/irda-app-remote-manager.hpp
  8. 95 0
      applications/irda/irda-app-signal.cpp
  9. 61 0
      applications/irda/irda-app-signal.h
  10. 0 56
      applications/irda/irda-app-transceiver.cpp
  11. 0 21
      applications/irda/irda-app-transceiver.hpp
  12. 13 4
      applications/irda/irda-app.cpp
  13. 10 3
      applications/irda/irda-app.hpp
  14. 0 434
      applications/irda/irda_app_old.c
  15. 19 10
      applications/irda/scene/irda-app-scene-edit-delete.cpp
  16. 17 13
      applications/irda/scene/irda-app-scene-learn-enter-name.cpp
  17. 21 15
      applications/irda/scene/irda-app-scene-learn-success.cpp
  18. 44 10
      applications/irda/scene/irda-app-scene-learn.cpp
  19. 2 2
      applications/irda/scene/irda-app-scene-remote.cpp
  20. 1 1
      applications/irda/scene/irda-app-scene-universal-common.cpp
  21. 65 94
      applications/irda_monitor/irda_monitor.c
  22. 32 8
      firmware/targets/api-hal-include/api-hal-irda.h
  23. 47 64
      firmware/targets/f5/api-hal/api-hal-irda.c
  24. 5 1
      firmware/targets/f5/api-hal/api-hal-resources.c
  25. 3 0
      firmware/targets/f5/api-hal/api-hal-resources.h
  26. 0 2
      firmware/targets/f5/api-hal/api-hal-tim.c
  27. 70 57
      firmware/targets/f6/api-hal/api-hal-irda.c
  28. 5 1
      firmware/targets/f6/api-hal/api-hal-resources.c
  29. 3 0
      firmware/targets/f6/api-hal/api-hal-resources.h
  30. 11 2
      lib/file_reader/file_reader.cpp
  31. 5 0
      lib/file_reader/file_reader.h
  32. 0 0
      lib/irda/encoder_decoder/irda.c
  33. 0 21
      lib/irda/encoder_decoder/irda.h
  34. 0 0
      lib/irda/encoder_decoder/irda_common_decoder.c
  35. 0 0
      lib/irda/encoder_decoder/irda_common_encoder.c
  36. 0 0
      lib/irda/encoder_decoder/irda_common_i.h
  37. 0 0
      lib/irda/encoder_decoder/irda_common_protocol_defs.c
  38. 0 0
      lib/irda/encoder_decoder/irda_i.h
  39. 0 0
      lib/irda/encoder_decoder/irda_protocol_defs_i.h
  40. 0 0
      lib/irda/encoder_decoder/nec/irda_decoder_nec.c
  41. 0 0
      lib/irda/encoder_decoder/nec/irda_encoder_nec.c
  42. 0 0
      lib/irda/encoder_decoder/rc6/irda_decoder_rc6.c
  43. 0 0
      lib/irda/encoder_decoder/rc6/irda_encoder_rc6.c
  44. 0 0
      lib/irda/encoder_decoder/samsung/irda_decoder_samsung.c
  45. 0 0
      lib/irda/encoder_decoder/samsung/irda_encoder_samsung.c
  46. 17 6
      lib/irda/worker/irda_transmit.c
  47. 41 0
      lib/irda/worker/irda_transmit.h
  48. 237 0
      lib/irda/worker/irda_worker.c
  49. 91 0
      lib/irda/worker/irda_worker.h
  50. 6 4
      lib/lib.mk

+ 121 - 53
applications/irda/cli/irda-cli.cpp

@@ -1,24 +1,48 @@
-#include "app-template.h"
-#include "cli/cli.h"
-#include "cmsis_os2.h"
+#include <api-hal-delay.h>
+#include <irda.h>
+#include <app-template.h>
+#include <cli/cli.h>
+#include <cmsis_os2.h>
+#include <irda_worker.h>
 #include <furi.h>
 #include <furi.h>
 #include <api-hal-irda.h>
 #include <api-hal-irda.h>
-#include "irda.h"
 #include <sstream>
 #include <sstream>
 #include <string>
 #include <string>
 #include <m-string.h>
 #include <m-string.h>
+#include <irda_transmit.h>
 
 
-typedef struct IrdaCli {
-    IrdaDecoderHandler* handler;
-    osMessageQueueId_t message_queue;
-} IrdaCli;
-
-static void irda_rx_callback(void* ctx, bool level, uint32_t duration) {
-    IrdaCli* irda_cli = (IrdaCli*)ctx;
-    const IrdaMessage* message;
-    message = irda_decode(irda_cli->handler, level, duration);
-    if(message) {
-        osMessageQueuePut(irda_cli->message_queue, message, 0, 0);
+static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
+    furi_assert(received_signal);
+    char buf[100];
+    size_t buf_cnt;
+    Cli* cli = (Cli*)context;
+
+    if(irda_worker_signal_is_decoded(received_signal)) {
+        const IrdaMessage* message = irda_worker_get_decoded_message(received_signal);
+        buf_cnt = sniprintf(
+            buf,
+            sizeof(buf),
+            "%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
+            irda_get_protocol_name(message->protocol),
+            irda_get_protocol_address_length(message->protocol),
+            message->address,
+            irda_get_protocol_command_length(message->protocol),
+            message->command,
+            message->repeat ? " R" : "");
+        cli_write(cli, (uint8_t*)buf, buf_cnt);
+    } else {
+        const uint32_t* timings;
+        size_t timings_cnt;
+        irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
+
+        buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt);
+        cli_write(cli, (uint8_t*)buf, buf_cnt);
+        for(size_t i = 0; i < timings_cnt; ++i) {
+            buf_cnt = sniprintf(buf, sizeof(buf), "%lu ", timings[i]);
+            cli_write(cli, (uint8_t*)buf, buf_cnt);
+        }
+        buf_cnt = sniprintf(buf, sizeof(buf), "\r\n");
+        cli_write(cli, (uint8_t*)buf, buf_cnt);
     }
     }
 }
 }
 
 
@@ -27,30 +51,19 @@ static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) {
         printf("IRDA is busy. Exit.");
         printf("IRDA is busy. Exit.");
         return;
         return;
     }
     }
-    IrdaCli irda_cli;
-    irda_cli.handler = irda_alloc_decoder();
-    irda_cli.message_queue = osMessageQueueNew(2, sizeof(IrdaMessage), NULL);
-    api_hal_irda_rx_irq_init();
-    api_hal_irda_rx_irq_set_callback(irda_rx_callback, &irda_cli);
+
+    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);
 
 
     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)) {
-        IrdaMessage message;
-        if(osOK == osMessageQueueGet(irda_cli.message_queue, &message, NULL, 50)) {
-            printf(
-                "%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
-                irda_get_protocol_name(message.protocol),
-                irda_get_protocol_address_length(message.protocol),
-                message.address,
-                irda_get_protocol_command_length(message.protocol),
-                message.command,
-                message.repeat ? " R" : "");
-        }
+        delay(50);
     }
     }
 
 
-    api_hal_irda_rx_irq_deinit();
-    irda_free_decoder(irda_cli.handler);
-    osMessageQueueDelete(irda_cli.message_queue);
+    irda_worker_stop(worker);
+    irda_worker_free(worker);
 }
 }
 
 
 static void irda_cli_print_usage(void) {
 static void irda_cli_print_usage(void) {
@@ -63,38 +76,93 @@ static void irda_cli_print_usage(void) {
     printf("\r\n");
     printf("\r\n");
 }
 }
 
 
-static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
-    if(api_hal_irda_rx_irq_is_busy()) {
-        printf("IRDA is busy. Exit.");
-        return;
-    }
-
+static bool parse_message(const char* str, IrdaMessage* message) {
     uint32_t command = 0;
     uint32_t command = 0;
     uint32_t address = 0;
     uint32_t address = 0;
     char protocol_name[32];
     char protocol_name[32];
-    int parsed = sscanf(string_get_cstr(args), "%31s %lX %lX", protocol_name, &address, &command);
+    int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &address, &command);
 
 
     if(parsed != 3) {
     if(parsed != 3) {
-        printf("Wrong arguments.\r\n");
-        irda_cli_print_usage();
-        return;
+        return false;
     }
     }
 
 
     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
 
 
     if(!irda_is_protocol_valid(protocol)) {
     if(!irda_is_protocol_valid(protocol)) {
-        printf("Unknown protocol.\r\n");
-        irda_cli_print_usage();
+        return false;
+    }
+
+    message->protocol = protocol;
+    message->address = address;
+    message->command = command;
+    message->repeat = false;
+
+    return true;
+}
+
+static bool parse_signal_raw(
+    const char* str,
+    uint32_t* timings,
+    uint32_t* timings_cnt,
+    float* duty_cycle,
+    float* frequency) {
+    char frequency_str[10];
+    char duty_cycle_str[10];
+    int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
+    if(parsed != 2) return false;
+
+    *frequency = atoi(frequency_str);
+    *duty_cycle = (float)atoi(duty_cycle_str) / 100;
+    str += strlen(frequency_str) + strlen(duty_cycle_str) + 10;
+
+    uint32_t timings_cnt_max = *timings_cnt;
+    *timings_cnt = 0;
+
+    while(1) {
+        char timing_str[10];
+        for(; *str == ' '; ++str)
+            ;
+        if(1 != sscanf(str, "%9s", timing_str)) break;
+        str += strlen(timing_str);
+        uint32_t timing = atoi(timing_str);
+        if(timing <= 0) break;
+        if(*timings_cnt >= timings_cnt_max) break;
+        timings[*timings_cnt] = timing;
+        ++*timings_cnt;
+    }
+
+    printf("\r\nTransmit:");
+    for(size_t i = 0; i < *timings_cnt; ++i) {
+        printf(" %ld", timings[i]);
+    }
+    printf("\r\n");
+
+    return true;
+}
+
+static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
+    if(api_hal_irda_rx_irq_is_busy()) {
+        printf("IRDA is busy. Exit.");
         return;
         return;
     }
     }
 
 
-    IrdaMessage message = {
-        .protocol = protocol,
-        .address = address,
-        .command = command,
-        .repeat = false,
-    };
-    irda_send(&message, 1);
+    IrdaMessage message;
+    const char* str = string_get_cstr(args);
+    float frequency;
+    float duty_cycle;
+    uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000);
+    uint32_t timings_cnt = 1000;
+
+    if(parse_message(str, &message)) {
+        irda_send(&message, 1);
+    } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) {
+        irda_send_raw_ext(timings, timings_cnt, true, duty_cycle, frequency);
+    } else {
+        printf("Wrong arguments.\r\n");
+        irda_cli_print_usage();
+    }
+
+    free(timings);
 }
 }
 
 
 extern "C" void irda_cli_init() {
 extern "C" void irda_cli_init() {

+ 8 - 8
applications/irda/irda-app-brute-force.cpp

@@ -16,7 +16,7 @@ bool IrdaAppBruteForce::calculate_messages() {
 
 
     file_parser.reset();
     file_parser.reset();
     while(1) {
     while(1) {
-        auto message = file_parser.read_message(&file);
+        auto message = file_parser.read_signal(&file);
         if(!message) break;
         if(!message) break;
         auto element = records.find(message->name);
         auto element = records.find(message->name);
         if(element != records.cend()) {
         if(element != records.cend()) {
@@ -37,19 +37,19 @@ void IrdaAppBruteForce::stop_bruteforce() {
 }
 }
 
 
 // TODO: [FL-1418] replace with timer-chained consequence of messages.
 // TODO: [FL-1418] replace with timer-chained consequence of messages.
-bool IrdaAppBruteForce::send_next_bruteforce(const IrdaAppSignalTransceiver& transceiver) {
+bool IrdaAppBruteForce::send_next_bruteforce(void) {
     furi_assert(current_record.size());
     furi_assert(current_record.size());
 
 
-    std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> message;
+    std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> file_signal;
 
 
     do {
     do {
-        message = file_parser.read_message(&file);
-    } while(message && current_record.compare(message->name));
+        file_signal = file_parser.read_signal(&file);
+    } while(file_signal && current_record.compare(file_signal->name));
 
 
-    if(message) {
-        transceiver.send_message(&message->message);
+    if(file_signal) {
+        file_signal->signal.transmit();
     }
     }
-    return !!message;
+    return !!file_signal;
 }
 }
 
 
 bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) {
 bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) {

+ 1 - 2
applications/irda/irda-app-brute-force.hpp

@@ -2,7 +2,6 @@
 #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.hpp"
-#include "irda-app-transceiver.hpp"
 
 
 
 
 class IrdaAppBruteForce {
 class IrdaAppBruteForce {
@@ -26,7 +25,7 @@ class IrdaAppBruteForce {
 public:
 public:
     bool calculate_messages();
     bool calculate_messages();
     void stop_bruteforce();
     void stop_bruteforce();
-    bool send_next_bruteforce(const IrdaAppSignalTransceiver& receiver);
+    bool send_next_bruteforce();
     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);
 
 

+ 150 - 8
applications/irda/irda-app-file-parser.cpp

@@ -1,26 +1,83 @@
 #include "irda-app-file-parser.hpp"
 #include "irda-app-file-parser.hpp"
+#include "irda-app-remote-manager.hpp"
+#include "irda-app-signal.h"
+#include <irda.h>
+#include <cstdio>
+#include <stdint.h>
+#include <string_view>
+#include <furi.h>
 
 
-std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> IrdaAppFileParser::read_message(File* file) {
+uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100);
+
+std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> IrdaAppFileParser::read_signal(File* file) {
     while(1) {
     while(1) {
         auto str = getline(file);
         auto str = getline(file);
         if(str.empty()) return nullptr;
         if(str.empty()) return nullptr;
 
 
-        auto message = parse_message(str);
+        auto message = parse_signal(str);
+        if(!message.get()) {
+            message = parse_signal_raw(str);
+        }
         if(message) return message;
         if(message) return message;
     }
     }
 }
 }
 
 
-std::unique_ptr<IrdaAppFileParser::IrdaFileMessage>
-    IrdaAppFileParser::parse_message(const std::string& str) const {
+bool IrdaAppFileParser::store_signal(File* file, const IrdaAppSignal& signal, const char* name) {
+    char* content = new char[max_line_length];
+    size_t written = 0;
+
+    if(!signal.is_raw()) {
+        auto message = signal.get_message();
+        auto protocol = message.protocol;
+
+        sniprintf(
+            content,
+            max_line_length,
+            "%.31s %.31s A:%0*lX C:%0*lX\n",
+            name,
+            irda_get_protocol_name(protocol),
+            irda_get_protocol_address_length(protocol),
+            message.address,
+            irda_get_protocol_command_length(protocol),
+            message.command);
+        written = strlen(content);
+    } else {
+        int duty_cycle = 100 * IRDA_COMMON_DUTY_CYCLE;
+        written += sniprintf(
+            &content[written],
+            max_line_length - written,
+            "%.31s RAW F:%d DC:%d",
+            name,
+            IRDA_COMMON_CARRIER_FREQUENCY,
+            duty_cycle);
+
+        auto& raw_signal = signal.get_raw_signal();
+        for(size_t i = 0; i < raw_signal.timings_cnt; ++i) {
+            written += sniprintf(
+                &content[written], max_line_length - written, " %ld", raw_signal.timings[i]);
+            furi_assert(written <= max_line_length);
+        }
+        written += snprintf(&content[written], max_line_length - written, "\n");
+    }
+    furi_assert(written < max_line_length);
+
+    size_t write_count = 0;
+    write_count = get_fs_api().file.write(file, content, written);
+    delete[] content;
+    return (file->error_id == FSE_OK) && (write_count == written);
+}
+
+std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
+    IrdaAppFileParser::parse_signal(const std::string& str) const {
     char protocol_name[32];
     char protocol_name[32];
     uint32_t address;
     uint32_t address;
     uint32_t command;
     uint32_t command;
-    auto irda_file_message = std::make_unique<IrdaFileMessage>();
+    auto irda_file_signal = std::make_unique<IrdaFileSignal>();
 
 
     int parsed = std::sscanf(
     int parsed = std::sscanf(
         str.c_str(),
         str.c_str(),
         "%31s %31s A:%lX C:%lX",
         "%31s %31s A:%lX C:%lX",
-        irda_file_message->name,
+        irda_file_signal->name,
         protocol_name,
         protocol_name,
         &address,
         &address,
         &command);
         &command);
@@ -47,12 +104,97 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileMessage>
         return nullptr;
         return nullptr;
     }
     }
 
 
-    irda_file_message->message = {
+    IrdaMessage message = {
         .protocol = protocol,
         .protocol = protocol,
         .address = address,
         .address = address,
         .command = command,
         .command = command,
         .repeat = false,
         .repeat = false,
     };
     };
 
 
-    return irda_file_message;
+    irda_file_signal->signal.set_message(&message);
+
+    return irda_file_signal;
+}
+
+const char* find_first_not_of(const char* str, char symbol) {
+    const char* str_start = nullptr;
+    while(str != str_start) {
+        str_start = str;
+        str = strchr(str, symbol);
+    }
+
+    return str;
+}
+
+std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
+    IrdaAppFileParser::parse_signal_raw(const std::string& string) const {
+    char protocol_name[32];
+    uint32_t frequency;
+    uint32_t duty_cycle;
+    int str_len = string.size();
+    std::string_view str(string.c_str());
+    auto irda_file_signal = std::make_unique<IrdaFileSignal>();
+
+    int parsed = std::sscanf(
+        str.data(),
+        "%31s %31s F:%ld DC:%ld",
+        irda_file_signal->name,
+        protocol_name,
+        &frequency,
+        &duty_cycle);
+
+    if(parsed != 4) {
+        return nullptr;
+    }
+
+    char dummy[100] = {0};
+    int header_len = 0;
+    header_len = sniprintf(
+        dummy,
+        sizeof(dummy),
+        "%.31s %.31s F:%ld DC:%ld",
+        irda_file_signal->name,
+        protocol_name,
+        frequency,
+        duty_cycle);
+
+    furi_assert(header_len < str_len);
+    str.remove_prefix(header_len);
+
+    /* move allocated timings into raw signal object */
+    IrdaAppSignal::RawSignal raw_signal = {.timings_cnt = 0, .timings = new uint32_t[500]};
+    bool result = false;
+
+    while(!str.empty()) {
+        char buf[10];
+        size_t index = str.find_first_not_of(' ', 1);
+        if(index == std::string_view::npos) {
+            result = true;
+            break;
+        }
+        str.remove_prefix(index);
+        parsed = std::sscanf(str.data(), "%9s", buf);
+        if(parsed != 1) {
+            break;
+        }
+        str.remove_prefix(strlen(buf));
+
+        int value = atoi(buf);
+        if(value <= 0) {
+            break;
+        }
+        raw_signal.timings[raw_signal.timings_cnt] = value;
+        ++raw_signal.timings_cnt;
+        if(raw_signal.timings_cnt >= 500) {
+            break;
+        }
+    }
+
+    if(result) {
+        irda_file_signal->signal.set_raw_signal(raw_signal.timings, raw_signal.timings_cnt);
+    } else {
+        (void)irda_file_signal.release();
+        delete[] raw_signal.timings;
+    }
+    return irda_file_signal;
 }
 }

+ 15 - 6
applications/irda/irda-app-file-parser.hpp

@@ -1,17 +1,26 @@
 #pragma once
 #pragma once
-#include "file_reader/file_reader.hpp"
-#include "irda.h"
+#include <file_reader/file_reader.h>
+#include <irda.h>
+#include "irda-app-remote-manager.hpp"
 
 
 class IrdaAppFileParser : public FileReader {
 class IrdaAppFileParser : public FileReader {
 public:
 public:
     typedef struct {
     typedef struct {
         char name[32];
         char name[32];
-        IrdaMessage message;
-    } IrdaFileMessage;
+        IrdaAppSignal signal;
+    } IrdaFileSignal;
 
 
-    std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> read_message(File* file);
+    IrdaAppFileParser() {
+        /* Assume we can save max 512 samples */
+        set_max_line_length(max_line_length);
+    }
+
+    std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> read_signal(File* file);
+    bool store_signal(File* file, const IrdaAppSignal& signal, const char* name);
 
 
 private:
 private:
-    std::unique_ptr<IrdaFileMessage> parse_message(const std::string& str) const;
+    static const uint32_t max_line_length;
+    std::unique_ptr<IrdaFileSignal> parse_signal(const std::string& str) const;
+    std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const;
 };
 };
 
 

+ 11 - 29
applications/irda/irda-app-remote-manager.cpp

@@ -33,16 +33,15 @@ static std::string
     }
     }
 }
 }
 
 
-bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) {
-    remote->buttons.emplace_back(button_name, message);
+bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaAppSignal& signal) {
+    remote->buttons.emplace_back(button_name, signal);
     return store();
     return store();
 }
 }
 
 
 bool IrdaAppRemoteManager::add_remote_with_button(
 bool IrdaAppRemoteManager::add_remote_with_button(
     const char* button_name,
     const char* button_name,
-    const IrdaMessage* message) {
+    const IrdaAppSignal& signal) {
     furi_check(button_name != nullptr);
     furi_check(button_name != nullptr);
-    furi_check(message != nullptr);
 
 
     std::vector<std::string> remote_list;
     std::vector<std::string> remote_list;
     bool result = get_remote_list(remote_list);
     bool result = get_remote_list(remote_list);
@@ -51,7 +50,7 @@ bool IrdaAppRemoteManager::add_remote_with_button(
     auto new_name = find_vacant_name(remote_list, default_remote_name);
     auto new_name = find_vacant_name(remote_list, default_remote_name);
 
 
     remote = std::make_unique<IrdaAppRemote>(new_name);
     remote = std::make_unique<IrdaAppRemote>(new_name);
-    return add_button(button_name, message);
+    return add_button(button_name, signal);
 }
 }
 
 
 IrdaAppRemote::IrdaAppRemote(const std::string& name)
 IrdaAppRemote::IrdaAppRemote(const std::string& name)
@@ -70,12 +69,12 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const {
     return name_vector;
     return name_vector;
 }
 }
 
 
-const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t index) const {
+const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const {
     furi_check(remote.get() != nullptr);
     furi_check(remote.get() != nullptr);
     auto& buttons = remote->buttons;
     auto& buttons = remote->buttons;
     furi_check(index < buttons.size());
     furi_check(index < buttons.size());
 
 
-    return &buttons.at(index).message;
+    return buttons.at(index).signal;
 }
 }
 
 
 std::string IrdaAppRemoteManager::make_filename(const std::string& name) const {
 std::string IrdaAppRemoteManager::make_filename(const std::string& name) const {
@@ -166,7 +165,6 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() {
 
 
 bool IrdaAppRemoteManager::store(void) {
 bool IrdaAppRemoteManager::store(void) {
     File file;
     File file;
-    uint16_t write_count;
     std::string dirname(std::string("/") + irda_directory);
     std::string dirname(std::string("/") + irda_directory);
 
 
     IrdaAppFileParser file_parser;
     IrdaAppFileParser file_parser;
@@ -186,25 +184,9 @@ bool IrdaAppRemoteManager::store(void) {
         return false;
         return false;
     }
     }
 
 
-    char content[128];
-
     for(const auto& button : remote->buttons) {
     for(const auto& button : remote->buttons) {
-        auto protocol = button.message.protocol;
-
-        sniprintf(
-            content,
-            sizeof(content),
-            "%.31s %.31s A:%0*lX C:%0*lX\n",
-            button.name.c_str(),
-            irda_get_protocol_name(protocol),
-            irda_get_protocol_address_length(protocol),
-            button.message.address,
-            irda_get_protocol_command_length(protocol),
-            button.message.command);
-
-        auto content_len = strlen(content);
-        write_count = file_parser.get_fs_api().file.write(&file, content, content_len);
-        if(file.error_id != FSE_OK || write_count != content_len) {
+        bool result = file_parser.store_signal(&file, button.signal, button.name.c_str());
+        if(!result) {
             file_parser.get_sd_api().show_error(
             file_parser.get_sd_api().show_error(
                 file_parser.get_sd_api().context, "Cannot write\nto key file");
                 file_parser.get_sd_api().context, "Cannot write\nto key file");
             file_parser.get_fs_api().file.close(&file);
             file_parser.get_fs_api().file.close(&file);
@@ -267,9 +249,9 @@ bool IrdaAppRemoteManager::load(const std::string& name) {
     remote = std::make_unique<IrdaAppRemote>(name);
     remote = std::make_unique<IrdaAppRemote>(name);
 
 
     while(1) {
     while(1) {
-        auto message = file_parser.read_message(&file);
-        if(!message) break;
-        remote->buttons.emplace_back(message->name, &message->message);
+        auto file_signal = file_parser.read_signal(&file);
+        if(!file_signal.get()) break;
+        remote->buttons.emplace_back(file_signal->name, file_signal->signal);
     }
     }
     file_parser.get_fs_api().file.close(&file);
     file_parser.get_fs_api().file.close(&file);
 
 

+ 9 - 6
applications/irda/irda-app-remote-manager.hpp

@@ -1,4 +1,5 @@
 #pragma once
 #pragma once
+#include <irda_worker.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -6,14 +7,16 @@
 #include <irda.h>
 #include <irda.h>
 #include <sd-card-api.h>
 #include <sd-card-api.h>
 #include <filesystem-api.h>
 #include <filesystem-api.h>
+#include "irda-app-signal.h"
+
 
 
 class IrdaAppRemoteButton {
 class IrdaAppRemoteButton {
     friend class IrdaAppRemoteManager;
     friend class IrdaAppRemoteManager;
     std::string name;
     std::string name;
-    IrdaMessage message;
+    IrdaAppSignal signal;
 public:
 public:
-    IrdaAppRemoteButton(const char* name, const IrdaMessage* message)
-        : name(name), message (*message) {}
+    IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal)
+        : name(name), signal (signal) {}
     ~IrdaAppRemoteButton() {}
     ~IrdaAppRemoteButton() {}
 };
 };
 
 
@@ -38,8 +41,8 @@ class IrdaAppRemoteManager {
     std::string make_filename(const std::string& name) const;
     std::string make_filename(const std::string& name) const;
 
 
 public:
 public:
-    bool add_remote_with_button(const char* button_name, const IrdaMessage* message);
-    bool add_button(const char* button_name, const IrdaMessage* message);
+    bool add_remote_with_button(const char* button_name, const IrdaAppSignal& signal);
+    bool add_button(const char* button_name, const IrdaAppSignal& signal);
 
 
     int find_remote_name(const std::vector<std::string>& strings);
     int find_remote_name(const std::vector<std::string>& strings);
     bool rename_button(uint32_t index, const char* str);
     bool rename_button(uint32_t index, const char* str);
@@ -50,7 +53,7 @@ public:
     std::string get_button_name(uint32_t index);
     std::string get_button_name(uint32_t index);
     std::string get_remote_name();
     std::string get_remote_name();
     size_t get_number_of_buttons();
     size_t get_number_of_buttons();
-    const IrdaMessage* get_button_data(size_t button_index) const;
+    const IrdaAppSignal& get_button_data(size_t index) const;
     bool delete_button(uint32_t index);
     bool delete_button(uint32_t index);
     bool delete_remote();
     bool delete_remote();
 
 

+ 95 - 0
applications/irda/irda-app-signal.cpp

@@ -0,0 +1,95 @@
+#include "irda-app-signal.h"
+#include <irda_transmit.h>
+
+void IrdaAppSignal::copy_timings(const uint32_t* timings, size_t size) {
+    furi_assert(size);
+    furi_assert(timings);
+
+    if(size) {
+        payload.raw.timings = new uint32_t[size];
+        payload.raw.timings_cnt = size;
+        memcpy(payload.raw.timings, timings, size * sizeof(uint32_t));
+    }
+}
+
+void IrdaAppSignal::clear_timings() {
+    if(!decoded) {
+        delete[] payload.raw.timings;
+        payload.raw.timings_cnt = 0;
+        payload.raw.timings = nullptr;
+    }
+}
+
+IrdaAppSignal::IrdaAppSignal(const uint32_t* timings, size_t timings_cnt) {
+    decoded = false;
+    copy_timings(timings, timings_cnt);
+}
+
+IrdaAppSignal::IrdaAppSignal(const IrdaMessage* irda_message) {
+    decoded = true;
+    payload.message = *irda_message;
+}
+
+IrdaAppSignal& IrdaAppSignal::operator=(const IrdaAppSignal& other) {
+    clear_timings();
+    decoded = other.decoded;
+    if(decoded) {
+        payload.message = other.payload.message;
+    } else {
+        copy_timings(other.payload.raw.timings, other.payload.raw.timings_cnt);
+    }
+
+    return *this;
+}
+
+IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) {
+    decoded = other.decoded;
+    if(decoded) {
+        payload.message = other.payload.message;
+    } else {
+        copy_timings(other.payload.raw.timings, other.payload.raw.timings_cnt);
+    }
+}
+
+IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) {
+    clear_timings();
+
+    decoded = other.decoded;
+    if(decoded) {
+        payload.message = other.payload.message;
+    } else {
+        furi_assert(other.payload.raw.timings_cnt > 0);
+
+        payload.raw.timings = other.payload.raw.timings;
+        payload.raw.timings_cnt = other.payload.raw.timings_cnt;
+        other.payload.raw.timings = nullptr;
+        other.payload.raw.timings_cnt = 0;
+    }
+}
+
+void IrdaAppSignal::set_message(const IrdaMessage* irda_message) {
+    clear_timings();
+    decoded = true;
+    payload.message = *irda_message;
+}
+
+void IrdaAppSignal::set_raw_signal(uint32_t* timings, size_t timings_cnt) {
+    clear_timings();
+    decoded = false;
+    payload.raw.timings = timings;
+    payload.raw.timings_cnt = timings_cnt;
+}
+
+void IrdaAppSignal::copy_raw_signal(uint32_t* timings, size_t timings_cnt) {
+    clear_timings();
+    decoded = false;
+    copy_timings(timings, timings_cnt);
+}
+
+void IrdaAppSignal::transmit() const {
+    if(decoded) {
+        irda_send(&payload.message, 1);
+    } else {
+        irda_send_raw(payload.raw.timings, payload.raw.timings_cnt, true);
+    }
+}

+ 61 - 0
applications/irda/irda-app-signal.h

@@ -0,0 +1,61 @@
+#pragma once
+#include <irda_worker.h>
+#include <stdint.h>
+#include <string>
+#include <irda.h>
+
+class IrdaAppSignal {
+public:
+    typedef struct {
+        size_t timings_cnt;
+        uint32_t* timings;
+    } RawSignal;
+
+private:
+    bool decoded;
+    union {
+        IrdaMessage message;
+        RawSignal raw;
+    } payload;
+
+    void copy_timings(const uint32_t* timings, size_t size);
+    void clear_timings();
+
+public:
+    IrdaAppSignal() {
+        decoded = true;
+        payload.message.protocol = IrdaProtocolUnknown;
+    }
+
+    ~IrdaAppSignal() {
+        clear_timings();
+    }
+
+    IrdaAppSignal(const uint32_t* timings, size_t timings_cnt);
+    IrdaAppSignal(const IrdaMessage* irda_message);
+
+    IrdaAppSignal(const IrdaAppSignal& other);
+    IrdaAppSignal(IrdaAppSignal&& other);
+
+    IrdaAppSignal& operator=(const IrdaAppSignal& signal);
+
+    void set_message(const IrdaMessage* irda_message);
+    void set_raw_signal(uint32_t* timings, size_t timings_cnt);
+    void copy_raw_signal(uint32_t* timings, size_t timings_cnt);
+
+    void transmit() const;
+
+    bool is_raw(void) const {
+        return !decoded;
+    }
+
+    const IrdaMessage& get_message(void) const {
+        furi_assert(decoded);
+        return payload.message;
+    }
+
+    const RawSignal& get_raw_signal(void) const {
+        furi_assert(!decoded);
+        return payload.raw;
+    }
+};

+ 0 - 56
applications/irda/irda-app-transceiver.cpp

@@ -1,56 +0,0 @@
-#include "irda-app.hpp"
-#include "irda.h"
-#include <api-hal-irda.h>
-
-void IrdaAppSignalTransceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) {
-    IrdaAppEvent event;
-    const IrdaMessage* irda_message;
-    IrdaAppSignalTransceiver* this_ = static_cast<IrdaAppSignalTransceiver*>(ctx);
-
-    irda_message = irda_decode(this_->decoder, level, duration);
-    if(irda_message) {
-        this_->message = *irda_message;
-        event.type = IrdaAppEvent::Type::IrdaMessageReceived;
-        osStatus_t result = osMessageQueuePut(this_->event_queue, &event, 0, 0);
-        furi_check(result == osOK);
-    }
-}
-
-IrdaAppSignalTransceiver::IrdaAppSignalTransceiver(void)
-    : capture_started(false)
-    , decoder(irda_alloc_decoder()) {
-}
-
-IrdaAppSignalTransceiver::~IrdaAppSignalTransceiver() {
-    capture_stop();
-    irda_free_decoder(decoder);
-}
-
-void IrdaAppSignalTransceiver::capture_once_start(osMessageQueueId_t queue) {
-    event_queue = queue;
-    irda_reset_decoder(decoder);
-    if(!capture_started) {
-        capture_started = true;
-        api_hal_irda_rx_irq_set_callback(IrdaAppSignalTransceiver::irda_rx_callback, this);
-        api_hal_irda_rx_irq_init();
-    }
-}
-
-void IrdaAppSignalTransceiver::capture_stop(void) {
-    IrdaAppEvent event;
-
-    if(capture_started) {
-        capture_started = false;
-        api_hal_irda_rx_irq_deinit();
-        while(osMessageQueueGet(this->event_queue, &event, 0, 0) == osOK)
-            ;
-    }
-}
-
-IrdaMessage* IrdaAppSignalTransceiver::get_last_message(void) {
-    return &message;
-}
-
-void IrdaAppSignalTransceiver::send_message(const IrdaMessage* message) const {
-    irda_send(message, 1);
-}

+ 0 - 21
applications/irda/irda-app-transceiver.hpp

@@ -1,21 +0,0 @@
-#pragma once
-#include <furi.h>
-#include <irda.h>
-
-class IrdaAppSignalTransceiver {
-public:
-    IrdaAppSignalTransceiver(void);
-    ~IrdaAppSignalTransceiver(void);
-    void capture_once_start(osMessageQueueId_t event_queue);
-    void capture_stop(void);
-    IrdaMessage* get_last_message(void);
-    void send_message(const IrdaMessage* message) const;
-
-private:
-    bool capture_started;
-    osMessageQueueId_t event_queue;
-    static void irda_rx_callback(void* ctx, bool level, uint32_t duration);
-    IrdaDecoderHandler* decoder;
-    IrdaMessage message;
-};
-

+ 13 - 4
applications/irda/irda-app.cpp

@@ -1,4 +1,5 @@
 #include "irda-app.hpp"
 #include "irda-app.hpp"
+#include <irda_worker.h>
 #include <furi.h>
 #include <furi.h>
 #include <gui/gui.h>
 #include <gui/gui.h>
 #include <input/input.h>
 #include <input/input.h>
@@ -99,10 +100,6 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() {
     return &remote_manager;
     return &remote_manager;
 }
 }
 
 
-IrdaAppSignalTransceiver* IrdaApp::get_transceiver() {
-    return &transceiver;
-}
-
 void IrdaApp::set_text_store(uint8_t index, const char* text...) {
 void IrdaApp::set_text_store(uint8_t index, const char* text...) {
     furi_check(index < text_store_max);
     furi_check(index < text_store_max);
 
 
@@ -220,3 +217,15 @@ void IrdaApp::notify_green_on() {
 void IrdaApp::notify_green_off() {
 void IrdaApp::notify_green_off() {
     notification_message(notification, &sequence_reset_green);
     notification_message(notification, &sequence_reset_green);
 }
 }
+
+IrdaWorker* IrdaApp::get_irda_worker() {
+    return irda_worker;
+}
+
+const IrdaAppSignal& IrdaApp::get_received_signal() const {
+    return received_signal;
+}
+
+void IrdaApp::set_received_signal(const IrdaAppSignal& signal) {
+    received_signal = signal;
+}

+ 10 - 3
applications/irda/irda-app.hpp

@@ -7,10 +7,10 @@
 #include "scene/irda-app-scene.hpp"
 #include "scene/irda-app-scene.hpp"
 #include "irda-app-view-manager.hpp"
 #include "irda-app-view-manager.hpp"
 #include "irda-app-remote-manager.hpp"
 #include "irda-app-remote-manager.hpp"
-#include "irda-app-transceiver.hpp"
 #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>
 
 
 
 
 class IrdaApp {
 class IrdaApp {
@@ -51,11 +51,15 @@ public:
     bool switch_to_previous_scene(uint8_t count = 1);
     bool switch_to_previous_scene(uint8_t count = 1);
     Scene get_previous_scene();
     Scene get_previous_scene();
     IrdaAppViewManager* get_view_manager();
     IrdaAppViewManager* get_view_manager();
-    IrdaAppSignalTransceiver* get_transceiver();
     void set_text_store(uint8_t index, const char* text...);
     void set_text_store(uint8_t index, const char* text...);
     char* get_text_store(uint8_t index);
     char* get_text_store(uint8_t index);
     uint8_t get_text_store_size();
     uint8_t get_text_store_size();
     IrdaAppRemoteManager* get_remote_manager();
     IrdaAppRemoteManager* get_remote_manager();
+
+    IrdaWorker* get_irda_worker();
+    const IrdaAppSignal& get_received_signal() const;
+    void set_received_signal(const IrdaAppSignal& signal);
+
     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list);
     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list);
 
 
     void set_edit_element(EditElement value);
     void set_edit_element(EditElement value);
@@ -87,8 +91,10 @@ public:
 
 
     IrdaApp() {
     IrdaApp() {
         notification = static_cast<NotificationApp*>(furi_record_open("notification"));
         notification = static_cast<NotificationApp*>(furi_record_open("notification"));
+        irda_worker = irda_worker_alloc();
     }
     }
     ~IrdaApp() {
     ~IrdaApp() {
+        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;
@@ -103,9 +109,10 @@ private:
     uint32_t current_button;
     uint32_t current_button;
 
 
     NotificationApp* notification;
     NotificationApp* notification;
-    IrdaAppSignalTransceiver transceiver;
     IrdaAppViewManager view_manager;
     IrdaAppViewManager view_manager;
     IrdaAppRemoteManager remote_manager;
     IrdaAppRemoteManager remote_manager;
+    IrdaWorker* irda_worker;
+    IrdaAppSignal received_signal;
 
 
     std::forward_list<Scene> previous_scenes_list;
     std::forward_list<Scene> previous_scenes_list;
     Scene current_scene = Scene::Start;
     Scene current_scene = Scene::Start;

+ 0 - 434
applications/irda/irda_app_old.c

@@ -1,434 +0,0 @@
-#include <furi.h>
-#include <api-hal.h>
-#include <gui/gui.h>
-#include <input/input.h>
-#include <cli/cli.h>
-#include <notification/notification-messages.h>
-
-#include <api-hal-irda.h>
-#include "irda.h"
-
-typedef enum {
-    EventTypeTick,
-    EventTypeKey,
-    EventTypeRX,
-} EventType;
-
-typedef IrdaMessage IrDAPacket;
-
-typedef struct {
-    union {
-        InputEvent input;
-        IrDAPacket rx;
-    } value;
-    EventType type;
-} AppEvent;
-
-//typedef struct {
-//    IrdaProtocol protocol;
-//    uint32_t address;
-//    uint32_t command;
-//} IrDAPacket;
-
-#define IRDA_PACKET_COUNT 8
-
-typedef struct {
-    osMessageQueueId_t cli_ir_rx_queue;
-    Cli* cli;
-    bool cli_cmd_is_active;
-} IrDAApp;
-
-typedef struct {
-    uint8_t mode_id;
-    uint16_t carrier_freq;
-    uint8_t carrier_duty_cycle_id;
-
-    uint8_t packet_id;
-    IrDAPacket packets[IRDA_PACKET_COUNT];
-} State;
-
-typedef void (*ModeInput)(AppEvent*, State*);
-typedef void (*ModeRender)(Canvas*, State*);
-
-void input_carrier(AppEvent* event, State* state);
-void render_carrier(Canvas* canvas, State* state);
-void input_packet(AppEvent* event, State* state);
-void render_packet(Canvas* canvas, State* state);
-
-typedef struct {
-    ModeRender render;
-    ModeInput input;
-} Mode;
-
-const Mode modes[] = {
-    {.render = render_carrier, .input = input_carrier},
-    {.render = render_packet, .input = input_packet},
-};
-
-const float duty_cycles[] = {0.1, 0.25, 0.333, 0.5, 1.0};
-
-void render_carrier(Canvas* canvas, State* state) {
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 2, 25, "carrier mode >");
-    canvas_draw_str(canvas, 2, 37, "? /\\ freq | \\/ duty cycle");
-    {
-        char buf[24];
-        sprintf(buf, "frequency: %u Hz", state->carrier_freq);
-        canvas_draw_str(canvas, 2, 50, buf);
-        sprintf(
-            buf, "duty cycle: %d/1000", (int)(duty_cycles[state->carrier_duty_cycle_id] * 1000));
-        canvas_draw_str(canvas, 2, 62, buf);
-    }
-}
-
-void input_carrier(AppEvent* event, State* state) {
-    if(event->value.input.key == InputKeyOk) {
-        if(event->value.input.type == InputTypePress) {
-            api_hal_irda_pwm_set(duty_cycles[state->carrier_duty_cycle_id], state->carrier_freq);
-        } else if(event->value.input.type == InputTypeRelease) {
-            api_hal_irda_pwm_stop();
-        }
-    }
-
-    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
-        if(state->carrier_freq < 45000) {
-            state->carrier_freq += 1000;
-        } else {
-            state->carrier_freq = 33000;
-        }
-    }
-
-    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
-        uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]);
-        if(state->carrier_duty_cycle_id < (duty_cycles_count - 1)) {
-            state->carrier_duty_cycle_id++;
-        } else {
-            state->carrier_duty_cycle_id = 0;
-        }
-    }
-}
-
-void render_packet(Canvas* canvas, State* state) {
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 2, 25, "< packet mode");
-    canvas_draw_str(canvas, 2, 37, "? /\\ \\/ packet");
-    {
-        char buf[30];
-        sprintf(
-            buf,
-            "P[%d]: %s 0x%lX 0x%lX",
-            state->packet_id,
-            irda_get_protocol_name(state->packets[state->packet_id].protocol),
-            state->packets[state->packet_id].address,
-            state->packets[state->packet_id].command);
-        canvas_draw_str(canvas, 2, 50, buf);
-    }
-}
-
-void input_packet(AppEvent* event, State* state) {
-    if(event->value.input.key == InputKeyOk) {
-        if(event->value.input.type == InputTypeShort) {
-            IrdaMessage message = {
-                .protocol = state->packets[state->packet_id].protocol,
-                .address = state->packets[state->packet_id].address,
-                .command = state->packets[state->packet_id].command,
-            };
-            irda_send(&message, 1);
-        }
-    }
-
-    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
-        if(state->packet_id < (IRDA_PACKET_COUNT - 1)) {
-            state->packet_id++;
-        };
-    }
-
-    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
-        if(state->packet_id > 0) {
-            state->packet_id--;
-        };
-    }
-}
-
-static void render_callback(Canvas* canvas, void* ctx) {
-    State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25);
-
-    if(state != NULL) {
-        canvas_clear(canvas);
-        canvas_set_color(canvas, ColorBlack);
-        canvas_set_font(canvas, FontPrimary);
-        canvas_draw_str(canvas, 2, 12, "irda test");
-
-        modes[state->mode_id].render(canvas, state);
-
-        release_mutex((ValueMutex*)ctx, state);
-    }
-}
-
-static void input_callback(InputEvent* input_event, void* ctx) {
-    osMessageQueueId_t event_queue = ctx;
-
-    AppEvent event;
-    event.type = EventTypeKey;
-    event.value.input = *input_event;
-    osMessageQueuePut(event_queue, &event, 0, 0);
-}
-
-void init_packet(
-    State* state,
-    uint8_t index,
-    IrdaProtocol protocol,
-    uint32_t address,
-    uint32_t command) {
-    if(index >= IRDA_PACKET_COUNT) return;
-    state->packets[index].protocol = protocol;
-    state->packets[index].address = address;
-    state->packets[index].command = command;
-}
-
-void irda_cli_cmd_rx(Cli* cli, string_t args, void* context) {
-    furi_assert(context);
-    IrDAPacket packet;
-    IrDAApp* app = context;
-    app->cli_cmd_is_active = true;
-    bool exit = false;
-
-    printf("Reading income packets...\r\nPress Ctrl+C to abort\r\n");
-    while(!exit) {
-        exit = cli_cmd_interrupt_received(app->cli);
-        osStatus status = osMessageQueueGet(app->cli_ir_rx_queue, &packet, 0, 5);
-        if(status == osOK) {
-            printf(
-                "%s "
-                "Address:0x%02X Command: 0x%02X %s\r\n",
-                irda_get_protocol_name(packet.protocol),
-                (uint8_t)packet.address,
-                (uint8_t)packet.command,
-                packet.repeat ? "R" : "");
-        }
-    }
-    printf("Interrupt command received");
-    app->cli_cmd_is_active = false;
-    return;
-}
-
-void irda_cli_cmd_tx(Cli* cli, string_t args, void* context) {
-    furi_assert(context);
-    ValueMutex* state_mutex = context;
-    // Read protocol name
-    IrdaProtocol protocol;
-    string_t protocol_str;
-    string_init(protocol_str);
-    size_t ws = string_search_char(args, ' ');
-    if(ws == STRING_FAILURE) {
-        printf("Invalid input. Use ir_tx PROTOCOL ADDRESS COMMAND");
-        string_clear(protocol_str);
-        return;
-    } else {
-        string_set_n(protocol_str, args, 0, ws);
-        string_right(args, ws);
-        string_strim(args);
-    }
-    if(!string_cmp_str(protocol_str, "NEC")) {
-        protocol = IrdaProtocolNEC;
-    } else if(!string_cmp_str(protocol_str, "SAMSUNG")) {
-        protocol = IrdaProtocolSamsung32;
-    } else {
-        printf("Incorrect protocol. Valid protocols: `NEC`, `SAMSUNG`");
-        string_clear(protocol_str);
-        return;
-    }
-    string_clear(protocol_str);
-    // Read address
-    uint16_t address = strtoul(string_get_cstr(args), NULL, 16);
-    ws = string_search_char(args, ' ');
-    if(!(ws == 4 || ws == 6)) {
-        printf("Invalid address format. Use 4 [0-F] hex digits in 0xXXXX or XXXX formats");
-        return;
-    }
-    string_right(args, ws);
-    string_strim(args);
-    // Read command
-    uint16_t command = strtoul(string_get_cstr(args), NULL, 16);
-    ws = string_search_char(args, '\0');
-    if(!(ws == 4 || ws == 6)) {
-        printf("Invalid command format. Use 4 [0-F] hex digits in 0xXXXX or XXXX formats");
-        return;
-    }
-
-    State* state = (State*)acquire_mutex(state_mutex, 25);
-    if(state == NULL) {
-        printf("IRDA resources busy\r\n");
-        return;
-    }
-
-    IrdaMessage message = {
-        .protocol = protocol,
-        .address = address,
-        .command = command,
-    };
-    irda_send(&message, 1);
-    release_mutex(state_mutex, state);
-    return;
-}
-
-typedef struct {
-    osMessageQueueId_t event_queue;
-    IrdaDecoderHandler* handler;
-} IsrContext;
-
-void irda_rx_callback(void* ctx, bool level, uint32_t duration) {
-    IsrContext* isr_context = ctx;
-    const IrdaMessage* message = irda_decode(isr_context->handler, level, duration);
-    AppEvent event;
-    event.type = EventTypeRX;
-
-    if(message) {
-        event.value.rx = *message;
-        furi_check(osMessageQueuePut(isr_context->event_queue, &event, 0, 0) == osOK);
-    }
-}
-
-int32_t irda2(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL);
-
-    State _state;
-    uint8_t mode_count = sizeof(modes) / sizeof(modes[0]);
-    uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]);
-
-    _state.carrier_duty_cycle_id = duty_cycles_count - 2;
-    _state.carrier_freq = 36000;
-    _state.mode_id = 0;
-    _state.packet_id = 0;
-
-    IrDAApp irda_app;
-    irda_app.cli = furi_record_open("cli");
-    irda_app.cli_ir_rx_queue = osMessageQueueNew(1, sizeof(IrDAPacket), NULL);
-    irda_app.cli_cmd_is_active = false;
-
-    NotificationApp* notification = furi_record_open("notification");
-
-    for(uint8_t i = 0; i < IRDA_PACKET_COUNT; i++) {
-        init_packet(&_state, i, 0, 0, 0);
-    }
-
-    init_packet(&_state, 0, IrdaProtocolNEC, 0x00, 0x11);
-    init_packet(&_state, 1, IrdaProtocolNEC, 0x08, 0x59);
-    init_packet(&_state, 2, IrdaProtocolNEC, 0x00, 0x10);
-    init_packet(&_state, 3, IrdaProtocolNEC, 0x00, 0x15);
-    init_packet(&_state, 4, IrdaProtocolNEC, 0x00, 0x25);
-    init_packet(&_state, 5, IrdaProtocolSamsung32, 0x0E, 0x0C);
-    init_packet(&_state, 6, IrdaProtocolSamsung32, 0x0E, 0x0D);
-    init_packet(&_state, 7, IrdaProtocolSamsung32, 0x0E, 0x0E);
-
-    ValueMutex state_mutex;
-    if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("cannot create mutex\r\n");
-        return 255;
-    }
-
-    ViewPort* view_port = view_port_alloc();
-
-    view_port_draw_callback_set(view_port, render_callback, &state_mutex);
-    view_port_input_callback_set(view_port, input_callback, event_queue);
-
-    cli_add_command(irda_app.cli, "ir_rx", irda_cli_cmd_rx, &irda_app);
-    cli_add_command(irda_app.cli, "ir_tx", irda_cli_cmd_tx, &state_mutex);
-
-    // Open GUI and register view_port
-    Gui* gui = furi_record_open("gui");
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-    IsrContext isr_context = {
-        .handler = irda_alloc_decoder(),
-        .event_queue = event_queue,
-    };
-    api_hal_irda_rx_irq_init();
-    api_hal_irda_rx_irq_set_callback(irda_rx_callback, &isr_context);
-
-    AppEvent event;
-    while(1) {
-        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 500);
-
-        if(event_status == osOK) {
-            if(event.type == EventTypeKey) {
-                State* state = (State*)acquire_mutex_block(&state_mutex);
-
-                // press events
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyBack) {
-                    release_mutex(&state_mutex, state);
-
-                    // remove all view_ports create by app
-                    gui_remove_view_port(gui, view_port);
-                    view_port_free(view_port);
-
-                    // free decoder
-                    delete_mutex(&state_mutex);
-                    osMessageQueueDelete(event_queue);
-                    osMessageQueueDelete(irda_app.cli_ir_rx_queue);
-                    cli_delete_command(irda_app.cli, "ir_rx");
-                    cli_delete_command(irda_app.cli, "ir_tx");
-                    furi_record_close("cli");
-                    furi_record_close("notification");
-                    api_hal_irda_rx_irq_deinit();
-                    irda_free_decoder(isr_context.handler);
-
-                    // exit
-                    return 0;
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyLeft) {
-                    if(state->mode_id > 0) {
-                        state->mode_id--;
-                    }
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyRight) {
-                    if(state->mode_id < (mode_count - 1)) {
-                        state->mode_id++;
-                    }
-                }
-
-                modes[state->mode_id].input(&event, state);
-
-                release_mutex(&state_mutex, state);
-                view_port_update(view_port);
-
-            } else if(event.type == EventTypeRX) {
-                notification_message(notification, &sequence_blink_red_10);
-
-                // save only if we in packet mode
-                State* state = (State*)acquire_mutex_block(&state_mutex);
-                IrDAPacket packet = event.value.rx;
-
-                if(state->mode_id == 1) {
-                    printf("P=%s ", irda_get_protocol_name(packet.protocol));
-                    printf("A=0x%02lX ", packet.address);
-                    printf("C=0x%02lX ", packet.command);
-                    if(packet.repeat) {
-                        printf("R");
-                    }
-                    printf("\r\n");
-                    // Save packet to state
-                    memcpy(&(state->packets[state->packet_id]), &packet, sizeof(IrDAPacket));
-                }
-                if(irda_app.cli_cmd_is_active) {
-                    // Send decoded packet to cli
-                    osMessageQueuePut(irda_app.cli_ir_rx_queue, &packet, 0, 0);
-                }
-
-                release_mutex(&state_mutex, state);
-                view_port_update(view_port);
-
-                // blink anyway
-                notification_message(notification, &sequence_blink_green_10);
-            }
-
-        } else {
-            // event timeout
-        }
-    }
-}

+ 19 - 10
applications/irda/scene/irda-app-scene-edit-delete.cpp

@@ -20,17 +20,26 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
     auto remote_manager = app->get_remote_manager();
     auto remote_manager = app->get_remote_manager();
 
 
     if(app->get_edit_element() == IrdaApp::EditElement::Button) {
     if(app->get_edit_element() == IrdaApp::EditElement::Button) {
-        auto message = remote_manager->get_button_data(app->get_current_button());
+        auto signal = remote_manager->get_button_data(app->get_current_button());
         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter);
         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter);
-        app->set_text_store(
-            0,
-            "%s\n%s\nA=0x%0*lX C=0x%0*lX",
-            remote_manager->get_button_name(app->get_current_button()).c_str(),
-            irda_get_protocol_name(message->protocol),
-            irda_get_protocol_address_length(message->protocol),
-            message->address,
-            irda_get_protocol_command_length(message->protocol),
-            message->command);
+        if(!signal.is_raw()) {
+            auto message = &signal.get_message();
+            app->set_text_store(
+                0,
+                "%s\n%s\nA=0x%0*lX C=0x%0*lX",
+                remote_manager->get_button_name(app->get_current_button()).c_str(),
+                irda_get_protocol_name(message->protocol),
+                irda_get_protocol_address_length(message->protocol),
+                message->address,
+                irda_get_protocol_command_length(message->protocol),
+                message->command);
+        } else {
+            app->set_text_store(
+                0,
+                "%s\nRAW\n%ld samples",
+                remote_manager->get_button_name(app->get_current_button()).c_str(),
+                signal.get_raw_signal().timings_cnt);
+        }
     } else {
     } else {
         dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter);
         dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter);
         app->set_text_store(
         app->set_text_store(

+ 17 - 13
applications/irda/scene/irda-app-scene-learn-enter-name.cpp

@@ -5,15 +5,20 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     TextInput* text_input = view_manager->get_text_input();
     TextInput* text_input = view_manager->get_text_input();
 
 
-    auto transceiver = app->get_transceiver();
-    auto message = transceiver->get_last_message();
-
-    app->set_text_store(
-        0,
-        "%.4s_%0*lX",
-        irda_get_protocol_name(message->protocol),
-        irda_get_protocol_command_length(message->protocol),
-        message->command);
+    auto signal = app->get_received_signal();
+
+    if(!signal.is_raw()) {
+        auto message = &signal.get_message();
+        app->set_text_store(
+            0,
+            "%.4s_%0*lX",
+            irda_get_protocol_name(message->protocol),
+            irda_get_protocol_command_length(message->protocol),
+            message->command);
+    } else {
+        auto raw_signal = signal.get_raw_signal();
+        app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt);
+    }
 
 
     text_input_set_header_text(text_input, "Name the key");
     text_input_set_header_text(text_input, "Name the key");
     text_input_set_result_callback(
     text_input_set_result_callback(
@@ -31,14 +36,13 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) {
 
 
     if(event->type == IrdaAppEvent::Type::TextEditDone) {
     if(event->type == IrdaAppEvent::Type::TextEditDone) {
         auto remote_manager = app->get_remote_manager();
         auto remote_manager = app->get_remote_manager();
-        auto transceiver = app->get_transceiver();
         bool result = false;
         bool result = false;
         if(app->get_learn_new_remote()) {
         if(app->get_learn_new_remote()) {
             result = remote_manager->add_remote_with_button(
             result = remote_manager->add_remote_with_button(
-                app->get_text_store(0), transceiver->get_last_message());
+                app->get_text_store(0), app->get_received_signal());
         } else {
         } else {
-            result = remote_manager->add_button(
-                app->get_text_store(0), transceiver->get_last_message());
+            result =
+                remote_manager->add_button(app->get_text_store(0), app->get_received_signal());
         }
         }
 
 
         if(!result) {
         if(!result) {

+ 21 - 15
applications/irda/scene/irda-app-scene-learn-success.cpp

@@ -17,19 +17,26 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
 
 
     app->notify_green_on();
     app->notify_green_on();
 
 
-    auto transceiver = app->get_transceiver();
-    auto message = transceiver->get_last_message();
+    auto signal = app->get_received_signal();
+
+    if(!signal.is_raw()) {
+        auto message = &signal.get_message();
+        app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
+        app->set_text_store(
+            1,
+            "A: 0x%0*lX\nC: 0x%0*lX\n",
+            irda_get_protocol_address_length(message->protocol),
+            message->address,
+            irda_get_protocol_command_length(message->protocol),
+            message->command);
+        dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter);
+        dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop);
+    } else {
+        dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
+        app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt);
+        dialog_ex_set_text(dialog_ex, app->get_text_store(0), 75, 23, AlignLeft, AlignTop);
+    }
 
 
-    app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
-    app->set_text_store(
-        1,
-        "A: 0x%0*lX\nC: 0x%0*lX\n",
-        irda_get_protocol_address_length(message->protocol),
-        message->address,
-        irda_get_protocol_command_length(message->protocol),
-        message->command);
-    dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter);
-    dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop);
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "Save");
     dialog_ex_set_right_button_text(dialog_ex, "Save");
     dialog_ex_set_center_button_text(dialog_ex, "Send");
     dialog_ex_set_center_button_text(dialog_ex, "Send");
@@ -50,9 +57,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
             break;
             break;
         case DialogExResultCenter: {
         case DialogExResultCenter: {
             app->notify_space_blink();
             app->notify_space_blink();
-            auto transceiver = app->get_transceiver();
-            auto message = transceiver->get_last_message();
-            irda_send(message, 1);
+            auto signal = app->get_received_signal();
+            signal.transmit();
             break;
             break;
         }
         }
         case DialogExResultRight:
         case DialogExResultRight:

+ 44 - 10
applications/irda/scene/irda-app-scene-learn.cpp

@@ -1,14 +1,40 @@
 #include "../irda-app.hpp"
 #include "../irda-app.hpp"
+#include "../irda-app-event.hpp"
+#include <irda_worker.h>
 
 
-void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
-    auto view_manager = app->get_view_manager();
-    auto transceiver = app->get_transceiver();
-    auto event_queue = view_manager->get_event_queue();
+static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
+    furi_assert(context);
+    furi_assert(received_signal);
+
+    IrdaApp* app = static_cast<IrdaApp*>(context);
 
 
-    transceiver->capture_once_start(event_queue);
+    if(irda_worker_signal_is_decoded(received_signal)) {
+        IrdaAppSignal signal(irda_worker_get_decoded_message(received_signal));
+        app->set_received_signal(signal);
+    } else {
+        const uint32_t* timings;
+        size_t timings_cnt;
+        irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
+        IrdaAppSignal signal(timings, timings_cnt);
+        app->set_received_signal(signal);
+    }
+
+    irda_worker_set_received_signal_callback(app->get_irda_worker(), NULL);
+    IrdaAppEvent event;
+    event.type = IrdaAppEvent::Type::IrdaMessageReceived;
+    auto view_manager = app->get_view_manager();
+    view_manager->send_event(&event);
+}
 
 
+void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
+    auto view_manager = app->get_view_manager();
     auto popup = view_manager->get_popup();
     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);
+
     popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31);
     popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31);
     popup_set_text(
     popup_set_text(
         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter);
@@ -24,19 +50,27 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
 bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) {
 bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) {
     bool consumed = false;
     bool consumed = false;
 
 
-    if(event->type == IrdaAppEvent::Type::Tick) {
+    switch(event->type) {
+    case IrdaAppEvent::Type::Tick:
         consumed = true;
         consumed = true;
         app->notify_red_blink();
         app->notify_red_blink();
-    }
-    if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) {
+        break;
+    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;
+    case IrdaAppEvent::Type::Back:
+        consumed = true;
+        irda_worker_stop(app->get_irda_worker());
+        app->switch_to_previous_scene();
+        break;
+    default:
+        furi_assert(0);
     }
     }
 
 
     return consumed;
     return consumed;
 }
 }
 
 
 void IrdaAppSceneLearn::on_exit(IrdaApp* app) {
 void IrdaAppSceneLearn::on_exit(IrdaApp* app) {
-    auto transceiver = app->get_transceiver();
-    transceiver->capture_stop();
 }
 }

+ 2 - 2
applications/irda/scene/irda-app-scene-remote.cpp

@@ -64,8 +64,8 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) {
         default:
         default:
             app->notify_click_and_blink();
             app->notify_click_and_blink();
             auto remote_manager = app->get_remote_manager();
             auto remote_manager = app->get_remote_manager();
-            auto message = remote_manager->get_button_data(event->payload.menu_index);
-            app->get_transceiver()->send_message(message);
+            auto signal = remote_manager->get_button_data(event->payload.menu_index);
+            signal.transmit();
             break;
             break;
         }
         }
     } else if(event->type == IrdaAppEvent::Type::Back) {
     } else if(event->type == IrdaAppEvent::Type::Back) {

+ 1 - 1
applications/irda/scene/irda-app-scene-universal-common.cpp

@@ -63,7 +63,7 @@ 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(*app->get_transceiver())) {
+            if(brute_force.send_next_bruteforce()) {
                 progress_popup(app);
                 progress_popup(app);
             } else {
             } else {
                 brute_force.stop_bruteforce();
                 brute_force.stop_bruteforce();

+ 65 - 94
applications/irda_monitor/irda_monitor.c

@@ -1,10 +1,11 @@
-#include "gui/canvas.h"
-#include "irda.h"
+#include <gui/canvas.h>
+#include <input/input.h>
+#include <irda.h>
+#include <irda_worker.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <furi.h>
 #include <furi.h>
 #include <api-hal-irda.h>
 #include <api-hal-irda.h>
 #include <api-hal.h>
 #include <api-hal.h>
-#include <notification/notification-messages.h>
 #include <gui/view_port.h>
 #include <gui/view_port.h>
 #include <gui/gui.h>
 #include <gui/gui.h>
 #include <gui/elements.h>
 #include <gui/elements.h>
@@ -20,25 +21,13 @@ typedef struct {
 } IrdaDelaysArray;
 } IrdaDelaysArray;
 
 
 typedef struct {
 typedef struct {
-    IrdaDecoderHandler* handler;
     char display_text[64];
     char display_text[64];
     osMessageQueueId_t event_queue;
     osMessageQueueId_t event_queue;
     IrdaDelaysArray delays;
     IrdaDelaysArray delays;
+    IrdaWorker* worker;
+    ViewPort* view_port;
 } IrdaMonitor;
 } IrdaMonitor;
 
 
-static void irda_rx_callback(void* ctx, bool level, uint32_t duration) {
-    IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx;
-    IrdaDelaysArray* delays = &irda_monitor->delays;
-
-    if(delays->timing_cnt > 1) furi_assert(level != delays->timing[delays->timing_cnt - 1].level);
-    delays->timing[delays->timing_cnt].level = level;
-    delays->timing[delays->timing_cnt].duration = duration;
-    delays->timing_cnt++; // Read-Modify-Write in ISR only: no need to add synchronization
-    if(delays->timing_cnt >= IRDA_TIMINGS_SIZE) {
-        delays->timing_cnt = 0;
-    }
-}
-
 void irda_monitor_input_callback(InputEvent* input_event, void* ctx) {
 void irda_monitor_input_callback(InputEvent* input_event, void* ctx) {
     furi_assert(ctx);
     furi_assert(ctx);
     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx;
     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx;
@@ -63,27 +52,70 @@ static void irda_monitor_draw_callback(Canvas* canvas, void* ctx) {
     }
     }
 }
 }
 
 
+static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
+    furi_assert(context);
+    furi_assert(received_signal);
+    IrdaMonitor* irda_monitor = context;
+
+    if(irda_worker_signal_is_decoded(received_signal)) {
+        const IrdaMessage* message = irda_worker_get_decoded_message(received_signal);
+        snprintf(
+            irda_monitor->display_text,
+            sizeof(irda_monitor->display_text),
+            "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
+            irda_get_protocol_name(message->protocol),
+            irda_get_protocol_address_length(message->protocol),
+            message->address,
+            irda_get_protocol_command_length(message->protocol),
+            message->command,
+            message->repeat ? " R" : "");
+        view_port_update(irda_monitor->view_port);
+        printf(
+            "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
+            irda_get_protocol_name(message->protocol),
+            irda_get_protocol_address_length(message->protocol),
+            message->address,
+            irda_get_protocol_command_length(message->protocol),
+            message->command,
+            message->repeat ? " R" : "");
+    } else {
+        const uint32_t* timings;
+        size_t timings_cnt;
+        irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt);
+        snprintf(
+            irda_monitor->display_text,
+            sizeof(irda_monitor->display_text),
+            "RAW\n%d samples\n",
+            timings_cnt);
+        view_port_update(irda_monitor->view_port);
+        printf("RAW, %d samples:\r\n", timings_cnt);
+        for(size_t i = 0; i < timings_cnt; ++i) {
+            printf("%lu ", timings[i]);
+        }
+        printf("\r\n");
+    }
+}
+
 int32_t irda_monitor_app(void* p) {
 int32_t irda_monitor_app(void* p) {
     (void)p;
     (void)p;
-    uint32_t counter = 0;
-    uint32_t print_counter = 0;
 
 
     IrdaMonitor* irda_monitor = furi_alloc(sizeof(IrdaMonitor));
     IrdaMonitor* irda_monitor = furi_alloc(sizeof(IrdaMonitor));
     irda_monitor->display_text[0] = 0;
     irda_monitor->display_text[0] = 0;
     irda_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL);
     irda_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL);
-    ViewPort* view_port = view_port_alloc();
-    IrdaDelaysArray* delays = &irda_monitor->delays;
-    NotificationApp* notification = furi_record_open("notification");
+    irda_monitor->view_port = view_port_alloc();
     Gui* gui = furi_record_open("gui");
     Gui* gui = furi_record_open("gui");
 
 
-    view_port_draw_callback_set(view_port, irda_monitor_draw_callback, irda_monitor);
-    view_port_input_callback_set(view_port, irda_monitor_input_callback, irda_monitor);
+    view_port_draw_callback_set(irda_monitor->view_port, irda_monitor_draw_callback, irda_monitor);
+    view_port_input_callback_set(
+        irda_monitor->view_port, irda_monitor_input_callback, irda_monitor);
 
 
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+    gui_add_view_port(gui, irda_monitor->view_port, GuiLayerFullscreen);
 
 
-    api_hal_irda_rx_irq_init();
-    api_hal_irda_rx_irq_set_callback(irda_rx_callback, irda_monitor);
-    irda_monitor->handler = irda_alloc_decoder();
+    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);
 
 
     while(1) {
     while(1) {
         InputEvent event;
         InputEvent event;
@@ -92,75 +124,14 @@ int32_t irda_monitor_app(void* p) {
                 break;
                 break;
             }
             }
         }
         }
-
-        if(counter != delays->timing_cnt) {
-            notification_message(notification, &sequence_blink_blue_10);
-        }
-
-        for(; counter != delays->timing_cnt;) {
-            const IrdaMessage* message = irda_decode(
-                irda_monitor->handler,
-                delays->timing[counter].level,
-                delays->timing[counter].duration);
-
-            ++counter;
-            if(counter >= IRDA_TIMINGS_SIZE) counter = 0;
-
-            if(message) {
-                snprintf(
-                    irda_monitor->display_text,
-                    sizeof(irda_monitor->display_text),
-                    "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
-                    irda_get_protocol_name(message->protocol),
-                    irda_get_protocol_address_length(message->protocol),
-                    message->address,
-                    irda_get_protocol_command_length(message->protocol),
-                    message->command,
-                    message->repeat ? " R" : "");
-                view_port_update(view_port);
-            }
-
-            size_t distance = (counter > print_counter) ?
-                                  counter - print_counter :
-                                  (counter + IRDA_TIMINGS_SIZE) - print_counter;
-            if(message || (distance > (IRDA_TIMINGS_SIZE / 2))) {
-                if(message) {
-                    printf(
-                        "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
-                        irda_get_protocol_name(message->protocol),
-                        irda_get_protocol_address_length(message->protocol),
-                        message->address,
-                        irda_get_protocol_command_length(message->protocol),
-                        message->command,
-                        message->repeat ? " R" : "");
-                } else {
-                    printf("== unknown data ==\r\n");
-                    snprintf(
-                        irda_monitor->display_text,
-                        sizeof(irda_monitor->display_text),
-                        "unknown data");
-                    view_port_update(view_port);
-                }
-                printf("{");
-                while(print_counter != counter) {
-                    printf("%lu, ", delays->timing[print_counter].duration);
-                    ++print_counter;
-                    if(print_counter >= IRDA_TIMINGS_SIZE) {
-                        print_counter = 0;
-                    }
-                }
-                printf("\r\n};\r\n");
-            }
-        }
     }
     }
 
 
-    api_hal_irda_rx_irq_deinit();
-    irda_free_decoder(irda_monitor->handler);
+    irda_worker_stop(irda_monitor->worker);
+    irda_worker_free(irda_monitor->worker);
     osMessageQueueDelete(irda_monitor->event_queue);
     osMessageQueueDelete(irda_monitor->event_queue);
-    view_port_enabled_set(view_port, false);
-    gui_remove_view_port(gui, view_port);
-    view_port_free(view_port);
-    furi_record_close("notification");
+    view_port_enabled_set(irda_monitor->view_port, false);
+    gui_remove_view_port(gui, irda_monitor->view_port);
+    view_port_free(irda_monitor->view_port);
     furi_record_close("gui");
     furi_record_close("gui");
     free(irda_monitor);
     free(irda_monitor);
 
 

+ 32 - 8
firmware/targets/api-hal-include/api-hal-irda.h

@@ -9,11 +9,18 @@ extern "C" {
 /**
 /**
  * Signature of callback function for receiving continuous IRDA rx signal.
  * Signature of callback function for receiving continuous IRDA rx signal.
  *
  *
- * @param   level - level of input IRDA rx signal
- * @param   duration - duration of continuous rx signal level in us
+ * @param   ctx[in] - context to pass to callback
+ * @param   level[in] - level of input IRDA rx signal
+ * @param   duration[in] - duration of continuous rx signal level in us
  */
  */
-typedef void (*TimerISRCallback)(void* ctx, bool level, uint32_t duration);
+typedef void (*ApiHalIrdaCaptureCallback)(void* ctx, bool level, uint32_t duration);
 
 
+/**
+ * Signature of callback function for reaching silence timeout on IRDA port.
+ *
+ * @param   ctx[in] - context to pass to callback
+ */
+typedef void (*ApiHalIrdaTimeoutCallback)(void* ctx);
 
 
 /**
 /**
  * Initialize IRDA RX timer to receive interrupts.
  * Initialize IRDA RX timer to receive interrupts.
@@ -27,20 +34,37 @@ void api_hal_irda_rx_irq_init(void);
  */
  */
 void api_hal_irda_rx_irq_deinit(void);
 void api_hal_irda_rx_irq_deinit(void);
 
 
+/** Setup api hal for receiving silence timeout.
+ * Should be used with 'api_hal_irda_timeout_irq_set_callback()'.
+ *
+ * @param[in]   timeout_ms - time to wait for silence on IRDA port
+ *                           before generating IRQ.
+ */
+void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms);
+
 /**
 /**
  * Setup callback for previously initialized IRDA RX interrupt.
  * Setup callback for previously initialized IRDA RX interrupt.
  *
  *
- * @param   callback - callback to call when RX signal edge changing occurs
- * @param   ctx - context for callback
+ * @param[in]   callback - callback to call when RX signal edge changing occurs
+ * @param[in]   ctx - context for callback
+ */
+void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx);
+
+/**
+ * Setup callback for reaching silence timeout on IRDA port.
+ * Should setup api hal with 'api_hal_irda_setup_rx_timeout_irq()' first.
+ *
+ * @param[in]   callback - callback for silence timeout
+ * @param[in]   ctx - context to pass to callback
  */
  */
-void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx);
+void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx);
 
 
 /**
 /**
  * Start generating IRDA TX PWM. Provides PWM initialization on
  * Start generating IRDA TX PWM. Provides PWM initialization on
  * defined frequency.
  * defined frequency.
  *
  *
- * @param   duty_cycle - duty cycle
- * @param   freq - PWM frequency to generate
+ * @param[in]   duty_cycle - duty cycle
+ * @param[in]   freq - PWM frequency to generate
  */
  */
 void api_hal_irda_pwm_set(float duty_cycle, float freq);
 void api_hal_irda_pwm_set(float duty_cycle, float freq);
 
 

+ 47 - 64
firmware/targets/f5/api-hal/api-hal-irda.c

@@ -1,82 +1,58 @@
-#include "cmsis_os.h"
-#include "api-hal-tim_i.h"
 #include "api-hal-irda.h"
 #include "api-hal-irda.h"
+#include <cmsis_os2.h>
+#include <api-hal-resources.h>
+
+#include <stdint.h>
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_gpio.h>
 #include <stm32wbxx_ll_gpio.h>
+
 #include <stdio.h>
 #include <stdio.h>
 #include <furi.h>
 #include <furi.h>
-#include "main.h"
-#include "api-hal-pwm.h"
-
+#include <main.h>
+#include <api-hal-pwm.h>
 
 
 static struct{
 static struct{
-    TimerISRCallback callback;
-    void *ctx;
+    ApiHalIrdaCaptureCallback capture_callback;
+    void *capture_context;
+    ApiHalIrdaTimeoutCallback timeout_callback;
+    void *timeout_context;
 } timer_irda;
 } timer_irda;
 
 
+typedef enum{
+    TimerIRQSourceCCI1,
+    TimerIRQSourceCCI2,
+} TimerIRQSource;
 
 
-void api_hal_irda_tim_isr(TimerIRQSource source)
-{
-    uint32_t duration = 0;
-    bool level = 0;
-
-    switch (source) {
-    case TimerIRQSourceCCI1:
-        duration = LL_TIM_OC_GetCompareCH1(TIM2);
-        LL_TIM_SetCounter(TIM2, 0);
-        level = 1;
-        break;
-    case TimerIRQSourceCCI2:
-        duration = LL_TIM_OC_GetCompareCH2(TIM2);
-        LL_TIM_SetCounter(TIM2, 0);
-        level = 0;
-        break;
-    default:
-        furi_check(0);
-    }
-
-    if (timer_irda.callback)
-        timer_irda.callback(timer_irda.ctx, level, duration);
-}
-
-void api_hal_irda_rx_irq_init(void)
-{
-    LL_TIM_InitTypeDef TIM_InitStruct = {0};
-
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    /* Peripheral clock enable */
+void api_hal_irda_rx_irq_init(void) {
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
-
     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-    /**TIM2 GPIO Configuration
-    PA0   ------> TIM2_CH1
-    */
-    GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
+    hal_gpio_init_ex(&gpio_irda_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
+
+    LL_TIM_InitTypeDef TIM_InitStruct = {0};
     TIM_InitStruct.Prescaler = 64 - 1;
     TIM_InitStruct.Prescaler = 64 - 1;
     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
-    TIM_InitStruct.Autoreload = 0xFFFFFFFF;
+    TIM_InitStruct.Autoreload = 0x7FFFFFFE;
     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
     LL_TIM_Init(TIM2, &TIM_InitStruct);
     LL_TIM_Init(TIM2, &TIM_InitStruct);
+
     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
-    LL_TIM_EnableARRPreload(TIM2);
+    LL_TIM_DisableARRPreload(TIM2);
+    LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1);
+    LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET);
+    LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
+    LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
+    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
+    LL_TIM_DisableIT_TRIG(TIM2);
+    LL_TIM_DisableDMAReq_TRIG(TIM2);
     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
-    LL_TIM_DisableMasterSlaveMode(TIM2);
+    LL_TIM_EnableMasterSlaveMode(TIM2);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
-    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING);
+    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
-    LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
-    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
 
 
     LL_TIM_EnableIT_CC1(TIM2);
     LL_TIM_EnableIT_CC1(TIM2);
     LL_TIM_EnableIT_CC2(TIM2);
     LL_TIM_EnableIT_CC2(TIM2);
@@ -90,22 +66,30 @@ void api_hal_irda_rx_irq_init(void)
     NVIC_EnableIRQ(TIM2_IRQn);
     NVIC_EnableIRQ(TIM2_IRQn);
 }
 }
 
 
+/* Doesn't work. F5 deprecated. */
 void api_hal_irda_rx_irq_deinit(void) {
 void api_hal_irda_rx_irq_deinit(void) {
-    LL_TIM_DisableIT_CC1(TIM2);
-    LL_TIM_DisableIT_CC2(TIM2);
-    LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH1);
-    LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
+    LL_TIM_DeInit(TIM2);
+}
+
+void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms) {
+    LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000);
+    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);
 }
 }
 
 
 bool api_hal_irda_rx_irq_is_busy(void) {
 bool api_hal_irda_rx_irq_is_busy(void) {
     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2));
     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2));
 }
 }
 
 
-void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) {
-    furi_check(callback);
+void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx) {
+    timer_irda.capture_callback = callback;
+    timer_irda.capture_context = ctx;
+}
 
 
-    timer_irda.callback = callback;
-    timer_irda.ctx = ctx;
+void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx) {
+    timer_irda.timeout_callback = callback;
+    timer_irda.timeout_context = ctx;
 }
 }
 
 
 void api_hal_irda_pwm_set(float value, float freq) {
 void api_hal_irda_pwm_set(float value, float freq) {
@@ -115,4 +99,3 @@ void api_hal_irda_pwm_set(float value, float freq) {
 void api_hal_irda_pwm_stop() {
 void api_hal_irda_pwm_stop() {
     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH);
     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH);
 }
 }
-

+ 5 - 1
firmware/targets/f5/api-hal/api-hal-resources.c

@@ -48,4 +48,8 @@ const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = GPIO_PIN_7};
 
 
 const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
 const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
 const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
 const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
-const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
+const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
+
+const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
+const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
+

+ 3 - 0
firmware/targets/f5/api-hal/api-hal-resources.h

@@ -87,6 +87,9 @@ extern const GpioPin gpio_rfid_pull;
 extern const GpioPin gpio_rfid_carrier_out;
 extern const GpioPin gpio_rfid_carrier_out;
 extern const GpioPin gpio_rfid_data_in;
 extern const GpioPin gpio_rfid_data_in;
 
 
+extern const GpioPin gpio_irda_rx;
+extern const GpioPin gpio_irda_tx;
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 0 - 2
firmware/targets/f5/api-hal/api-hal-tim.c

@@ -14,7 +14,6 @@ void TIM2_IRQHandler(void)
 
 
             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
                 // input capture
                 // input capture
-                api_hal_irda_tim_isr(TimerIRQSourceCCI1);
                 consumed = true;
                 consumed = true;
             }
             }
             else {
             else {
@@ -30,7 +29,6 @@ void TIM2_IRQHandler(void)
 
 
             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
                 // input capture
                 // input capture
-                api_hal_irda_tim_isr(TimerIRQSourceCCI2);
                 consumed = true;
                 consumed = true;
             }
             }
             else {
             else {

+ 70 - 57
firmware/targets/f6/api-hal/api-hal-irda.c

@@ -1,17 +1,22 @@
-#include "api-hal-interrupt.h"
 #include "api-hal-irda.h"
 #include "api-hal-irda.h"
+#include <cmsis_os2.h>
+#include <api-hal-interrupt.h>
+#include <api-hal-resources.h>
 
 
+#include <stdint.h>
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_gpio.h>
 #include <stm32wbxx_ll_gpio.h>
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <furi.h>
 #include <furi.h>
-#include "main.h"
-#include "api-hal-pwm.h"
+#include <main.h>
+#include <api-hal-pwm.h>
 
 
 static struct{
 static struct{
-    TimerISRCallback callback;
-    void *ctx;
+    ApiHalIrdaCaptureCallback capture_callback;
+    void *capture_context;
+    ApiHalIrdaTimeoutCallback timeout_callback;
+    void *timeout_context;
 } timer_irda;
 } timer_irda;
 
 
 typedef enum{
 typedef enum{
@@ -19,107 +24,105 @@ typedef enum{
     TimerIRQSourceCCI2,
     TimerIRQSourceCCI2,
 } TimerIRQSource;
 } TimerIRQSource;
 
 
-static void api_hal_irda_handle_capture(TimerIRQSource source)
-{
+static void api_hal_irda_handle_timeout(void) {
+    /* Timers CNT register starts to counting from 0 to ARR, but it is
+     * reseted when Channel 1 catches interrupt. It is not reseted by
+     * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR).
+     * This can cause false timeout: when time is over, but we started
+     * receiving new signal few microseconds ago, because CNT register
+     * is reseted once per period, not per sample. */
+    if (LL_GPIO_IsInputPinSet(gpio_irda_rx.port, gpio_irda_rx.pin) == 0)
+        return;
+
+    if (timer_irda.timeout_callback)
+        timer_irda.timeout_callback(timer_irda.timeout_context);
+}
+
+/* High pin level is a Space state of IRDA signal. Invert level for further processing. */
+static void api_hal_irda_handle_capture(TimerIRQSource source) {
     uint32_t duration = 0;
     uint32_t duration = 0;
     bool level = 0;
     bool level = 0;
 
 
     switch (source) {
     switch (source) {
     case TimerIRQSourceCCI1:
     case TimerIRQSourceCCI1:
-        duration = LL_TIM_OC_GetCompareCH1(TIM2);
-        LL_TIM_SetCounter(TIM2, 0);
-        level = 0;
+        duration = LL_TIM_IC_GetCaptureCH1(TIM2) - LL_TIM_IC_GetCaptureCH2(TIM2);
+        level = 1;
         break;
         break;
     case TimerIRQSourceCCI2:
     case TimerIRQSourceCCI2:
-        duration = LL_TIM_OC_GetCompareCH2(TIM2);
-        LL_TIM_SetCounter(TIM2, 0);
-        level = 1;
+        duration = LL_TIM_IC_GetCaptureCH2(TIM2);
+        level = 0;
         break;
         break;
     default:
     default:
         furi_check(0);
         furi_check(0);
     }
     }
 
 
-    if (timer_irda.callback)
-        timer_irda.callback(timer_irda.ctx, level, duration);
+    if (timer_irda.capture_callback)
+        timer_irda.capture_callback(timer_irda.capture_context, level, duration);
 }
 }
 
 
 static void api_hal_irda_isr() {
 static void api_hal_irda_isr() {
-    if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) {
+    if(LL_TIM_IsActiveFlag_CC3(TIM2)) {
+        LL_TIM_ClearFlag_CC3(TIM2);
+        api_hal_irda_handle_timeout();
+    }
+    if(LL_TIM_IsActiveFlag_CC1(TIM2)) {
         LL_TIM_ClearFlag_CC1(TIM2);
         LL_TIM_ClearFlag_CC1(TIM2);
 
 
         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
             // input capture
             // input capture
             api_hal_irda_handle_capture(TimerIRQSourceCCI1);
             api_hal_irda_handle_capture(TimerIRQSourceCCI1);
-        } else {
-            // output compare
-            //  HAL_TIM_OC_DelayElapsedCallback(htim);
-            //  HAL_TIM_PWM_PulseFinishedCallback(htim);
         }
         }
     }
     }
-    if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) {
+    if(LL_TIM_IsActiveFlag_CC2(TIM2)) {
         LL_TIM_ClearFlag_CC2(TIM2);
         LL_TIM_ClearFlag_CC2(TIM2);
 
 
         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
             // input capture
             // input capture
             api_hal_irda_handle_capture(TimerIRQSourceCCI2);
             api_hal_irda_handle_capture(TimerIRQSourceCCI2);
-        } else {
-            // output compare
-            //  HAL_TIM_OC_DelayElapsedCallback(htim);
-            //  HAL_TIM_PWM_PulseFinishedCallback(htim);
         }
         }
     }
     }
 }
 }
 
 
-void api_hal_irda_rx_irq_init(void)
-{
-    LL_TIM_InitTypeDef TIM_InitStruct = {0};
-
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    /* Peripheral clock enable */
+void api_hal_irda_rx_irq_init(void) {
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
-
     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-    /**TIM2 GPIO Configuration
-    PA0   ------> TIM2_CH1
-    */
-    GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
+    hal_gpio_init_ex(&gpio_irda_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
+
+    LL_TIM_InitTypeDef TIM_InitStruct = {0};
     TIM_InitStruct.Prescaler = 64 - 1;
     TIM_InitStruct.Prescaler = 64 - 1;
     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
-    TIM_InitStruct.Autoreload = 0xFFFFFFFF;
+    TIM_InitStruct.Autoreload = 0x7FFFFFFE;
     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
     LL_TIM_Init(TIM2, &TIM_InitStruct);
     LL_TIM_Init(TIM2, &TIM_InitStruct);
+
     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
-    LL_TIM_EnableARRPreload(TIM2);
+    LL_TIM_DisableARRPreload(TIM2);
+    LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1);
+    LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET);
+    LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
+    LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
+    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
+    LL_TIM_DisableIT_TRIG(TIM2);
+    LL_TIM_DisableDMAReq_TRIG(TIM2);
     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
-    LL_TIM_DisableMasterSlaveMode(TIM2);
+    LL_TIM_EnableMasterSlaveMode(TIM2);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
-    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING);
+    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI);
     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
-    LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
-    LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
 
 
     LL_TIM_EnableIT_CC1(TIM2);
     LL_TIM_EnableIT_CC1(TIM2);
     LL_TIM_EnableIT_CC2(TIM2);
     LL_TIM_EnableIT_CC2(TIM2);
     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
 
 
+    api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr);
+
     LL_TIM_SetCounter(TIM2, 0);
     LL_TIM_SetCounter(TIM2, 0);
     LL_TIM_EnableCounter(TIM2);
     LL_TIM_EnableCounter(TIM2);
-
-    api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr);
-    NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
-    NVIC_EnableIRQ(TIM2_IRQn);
 }
 }
 
 
 void api_hal_irda_rx_irq_deinit(void) {
 void api_hal_irda_rx_irq_deinit(void) {
@@ -127,15 +130,25 @@ void api_hal_irda_rx_irq_deinit(void) {
     api_hal_interrupt_set_timer_isr(TIM2, NULL);
     api_hal_interrupt_set_timer_isr(TIM2, NULL);
 }
 }
 
 
+void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms) {
+    LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000);
+    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);
+}
+
 bool api_hal_irda_rx_irq_is_busy(void) {
 bool api_hal_irda_rx_irq_is_busy(void) {
     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2));
     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2));
 }
 }
 
 
-void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) {
-    furi_check(callback);
+void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx) {
+    timer_irda.capture_callback = callback;
+    timer_irda.capture_context = ctx;
+}
 
 
-    timer_irda.callback = callback;
-    timer_irda.ctx = ctx;
+void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx) {
+    timer_irda.timeout_callback = callback;
+    timer_irda.timeout_context = ctx;
 }
 }
 
 
 void api_hal_irda_pwm_set(float value, float freq) {
 void api_hal_irda_pwm_set(float value, float freq) {

+ 5 - 1
firmware/targets/f6/api-hal/api-hal-resources.c

@@ -47,4 +47,8 @@ const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = GPIO_PIN_7};
 
 
 const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
 const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
 const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
 const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
-const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
+const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
+
+const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
+const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
+

+ 3 - 0
firmware/targets/f6/api-hal/api-hal-resources.h

@@ -86,6 +86,9 @@ extern const GpioPin gpio_rfid_pull;
 extern const GpioPin gpio_rfid_carrier_out;
 extern const GpioPin gpio_rfid_carrier_out;
 extern const GpioPin gpio_rfid_data_in;
 extern const GpioPin gpio_rfid_data_in;
 
 
+extern const GpioPin gpio_irda_rx;
+extern const GpioPin gpio_irda_tx;
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 11 - 2
lib/file_reader/file_reader.cpp

@@ -1,9 +1,10 @@
-#include "file_reader/file_reader.hpp"
+#include <file_reader.h>
 
 
 std::string FileReader::getline(File* file) {
 std::string FileReader::getline(File* file) {
     std::string str;
     std::string str;
     size_t newline_index = 0;
     size_t newline_index = 0;
     bool found_eol = false;
     bool found_eol = false;
+    bool max_length_exceeded = false;
 
 
     while(1) {
     while(1) {
         if(file_buf_cnt > 0) {
         if(file_buf_cnt > 0) {
@@ -20,7 +21,12 @@ std::string FileReader::getline(File* file) {
                 furi_assert(0);
                 furi_assert(0);
             }
             }
 
 
-            str.append(file_buf, end_index);
+            if (max_line_length && (str.size() + end_index > max_line_length))
+                max_length_exceeded = true;
+
+            if (!max_length_exceeded)
+                str.append(file_buf, end_index);
+
             memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
             memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
             file_buf_cnt = file_buf_cnt - end_index;
             file_buf_cnt = file_buf_cnt - end_index;
             if(found_eol) break;
             if(found_eol) break;
@@ -33,6 +39,9 @@ std::string FileReader::getline(File* file) {
         }
         }
     }
     }
 
 
+    if (max_length_exceeded)
+        str.clear();
+
     return str;
     return str;
 }
 }
 
 

+ 5 - 0
lib/file_reader/file_reader.hpp → lib/file_reader/file_reader.h

@@ -8,6 +8,7 @@ class FileReader {
 private:
 private:
     char file_buf[48];
     char file_buf[48];
     size_t file_buf_cnt = 0;
     size_t file_buf_cnt = 0;
+    size_t max_line_length = 0;
     SdCard_Api* sd_ex_api;
     SdCard_Api* sd_ex_api;
     FS_Api* fs_api;
     FS_Api* fs_api;
 
 
@@ -35,5 +36,9 @@ public:
     FS_Api& get_fs_api() {
     FS_Api& get_fs_api() {
         return *fs_api;
         return *fs_api;
     }
     }
+
+    void set_max_line_length(size_t value) {
+        max_line_length = value;
+    }
 };
 };
 
 

+ 0 - 0
lib/irda/irda.c → lib/irda/encoder_decoder/irda.c


+ 0 - 21
lib/irda/irda.h → lib/irda/encoder_decoder/irda.h

@@ -69,14 +69,6 @@ void irda_free_decoder(IrdaDecoderHandler* handler);
  */
  */
 void irda_reset_decoder(IrdaDecoderHandler* handler);
 void irda_reset_decoder(IrdaDecoderHandler* handler);
 
 
-/**
- * Send message over IRDA.
- *
- * \param[in]   message     - message to send.
- * \param[in]   times       - number of times message should be sent.
- */
-void irda_send(const IrdaMessage* message, int times);
-
 /**
 /**
  * Get protocol name by protocol enum.
  * Get protocol name by protocol enum.
  *
  *
@@ -117,19 +109,6 @@ uint8_t irda_get_protocol_command_length(IrdaProtocol protocol);
  */
  */
 bool irda_is_protocol_valid(IrdaProtocol protocol);
 bool irda_is_protocol_valid(IrdaProtocol protocol);
 
 
-/**
- * Send raw data through infrared port.
- *
- * \param[in]   protocol - use IRDA settings (duty cycle, frequency) from
- *              this protocol. If provided IrdaProtocolUnknown - use
- *              default settings.
- * \param[in]   timings - array of timings to send.
- * \param[in]   timings_cnt - timings array size.
- * \param[in]   start_from_mark - true if timings starts from mark,
- *              otherwise from space
- */
-void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark);
-
 /**
 /**
  * Allocate IRDA encoder.
  * Allocate IRDA encoder.
  *
  *

+ 0 - 0
lib/irda/irda_common_decoder.c → lib/irda/encoder_decoder/irda_common_decoder.c


+ 0 - 0
lib/irda/irda_common_encoder.c → lib/irda/encoder_decoder/irda_common_encoder.c


+ 0 - 0
lib/irda/irda_common_i.h → lib/irda/encoder_decoder/irda_common_i.h


+ 0 - 0
lib/irda/irda_common_protocol_defs.c → lib/irda/encoder_decoder/irda_common_protocol_defs.c


+ 0 - 0
lib/irda/irda_i.h → lib/irda/encoder_decoder/irda_i.h


+ 0 - 0
lib/irda/irda_protocol_defs_i.h → lib/irda/encoder_decoder/irda_protocol_defs_i.h


+ 0 - 0
lib/irda/nec/irda_decoder_nec.c → lib/irda/encoder_decoder/nec/irda_decoder_nec.c


+ 0 - 0
lib/irda/nec/irda_encoder_nec.c → lib/irda/encoder_decoder/nec/irda_encoder_nec.c


+ 0 - 0
lib/irda/rc6/irda_decoder_rc6.c → lib/irda/encoder_decoder/rc6/irda_decoder_rc6.c


+ 0 - 0
lib/irda/rc6/irda_encoder_rc6.c → lib/irda/encoder_decoder/rc6/irda_encoder_rc6.c


+ 0 - 0
lib/irda/samsung/irda_decoder_samsung.c → lib/irda/encoder_decoder/samsung/irda_decoder_samsung.c


+ 0 - 0
lib/irda/samsung/irda_encoder_samsung.c → lib/irda/encoder_decoder/samsung/irda_encoder_samsung.c


+ 17 - 6
lib/irda/irda_transmit.c → lib/irda/worker/irda_transmit.c

@@ -6,9 +6,11 @@
 #include <api-hal-irda.h>
 #include <api-hal-irda.h>
 #include <api-hal-delay.h>
 #include <api-hal-delay.h>
 
 
-static void irda_set_tx(uint32_t duration, bool level) {
+#define IRDA_SET_TX_COMMON(d, l)        irda_set_tx((d), (l), IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY)
+
+static void irda_set_tx(uint32_t duration, bool level, float duty_cycle, float frequency) {
     if (level) {
     if (level) {
-        api_hal_irda_pwm_set(IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY);
+        api_hal_irda_pwm_set(duty_cycle, frequency);
         delay_us(duration);
         delay_us(duration);
     } else {
     } else {
         api_hal_irda_pwm_stop();
         api_hal_irda_pwm_stop();
@@ -16,12 +18,21 @@ static void irda_set_tx(uint32_t duration, bool level) {
     }
     }
 }
 }
 
 
+void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency) {
+    __disable_irq();
+    for (uint32_t i = 0; i < timings_cnt; ++i) {
+        irda_set_tx(timings[i], (i % 2) ^ start_from_mark, duty_cycle, frequency);
+    }
+    IRDA_SET_TX_COMMON(0, false);
+    __enable_irq();
+}
+
 void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) {
 void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) {
     __disable_irq();
     __disable_irq();
     for (uint32_t i = 0; i < timings_cnt; ++i) {
     for (uint32_t i = 0; i < timings_cnt; ++i) {
-        irda_set_tx(timings[i], (i % 2) ^ start_from_mark);
+        IRDA_SET_TX_COMMON(timings[i], (i % 2) ^ start_from_mark);
     }
     }
-    irda_set_tx(0, false);
+    IRDA_SET_TX_COMMON(0, false);
     __enable_irq();
     __enable_irq();
 }
 }
 
 
@@ -49,7 +60,7 @@ void irda_send(const IrdaMessage* message, int times) {
     while (times) {
     while (times) {
         status = irda_encode(handler, &duration, &level);
         status = irda_encode(handler, &duration, &level);
         if (status != IrdaStatusError) {
         if (status != IrdaStatusError) {
-            irda_set_tx(duration, level);
+            IRDA_SET_TX_COMMON(duration, level);
         } else {
         } else {
             furi_assert(0);
             furi_assert(0);
             break;
             break;
@@ -58,7 +69,7 @@ void irda_send(const IrdaMessage* message, int times) {
             --times;
             --times;
     }
     }
 
 
-    irda_set_tx(0, false);
+    IRDA_SET_TX_COMMON(0, false);
     __enable_irq();
     __enable_irq();
 
 
     irda_free_encoder(handler);
     irda_free_encoder(handler);

+ 41 - 0
lib/irda/worker/irda_transmit.h

@@ -0,0 +1,41 @@
+#include <api-hal-irda.h>
+#include <irda.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Send message over IRDA.
+ *
+ * \param[in]   message     - message to send.
+ * \param[in]   times       - number of times message should be sent.
+ */
+void irda_send(const IrdaMessage* message, int times);
+
+/**
+ * Send raw data through infrared port.
+ *
+ * \param[in]   timings - array of timings to send.
+ * \param[in]   timings_cnt - timings array size.
+ * \param[in]   start_from_mark - true if timings starts from mark,
+ *              otherwise from space
+ */
+void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark);
+
+/**
+ * Send raw data through infrared port, with additional settings.
+ *
+ * \param[in]   timings - array of timings to send.
+ * \param[in]   timings_cnt - timings array size.
+ * \param[in]   start_from_mark - true if timings starts from mark,
+ *              otherwise from space
+ * \param[in]   duty_cycle - duty cycle to generate on PWM
+ * \param[in]   frequency - frequency to generate on PWM
+ */
+void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency);
+
+#ifdef __cplusplus
+}
+#endif
+

+ 237 - 0
lib/irda/worker/irda_worker.c

@@ -0,0 +1,237 @@
+#include "irda_worker.h"
+#include <irda.h>
+#include <api-hal-irda.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stream_buffer.h>
+#include <furi.h>
+#include <notification/notification-messages.h>
+
+#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
+
+struct IrdaWorkerSignal {
+    bool decoded;
+    size_t timings_cnt;
+    union {
+        IrdaMessage message;
+        uint32_t timings[MAX_TIMINGS_AMOUNT];
+    } data;
+};
+
+struct IrdaWorker {
+    FuriThread* thread;
+    IrdaDecoderHandler* irda_decoder;
+    StreamBufferHandle_t stream;
+
+    TaskHandle_t worker_handle;
+    IrdaWorkerSignal signal;
+
+    IrdaWorkerReceivedSignalCallback received_signal_callback;
+    void* context;
+    bool blink_enable;
+    bool overrun;
+    NotificationApp* notification;
+};
+
+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);
+}
+
+static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) {
+    IrdaWorker* instance = context;
+
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    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);
+    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+}
+
+static void irda_worker_process_timeout(IrdaWorker* instance) {
+    if (instance->signal.timings_cnt < 2)
+        return;
+
+    instance->signal.decoded = false;
+    if (instance->received_signal_callback)
+        instance->received_signal_callback(instance->context, &instance->signal);
+}
+
+static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, bool level) {
+    const IrdaMessage* message_decoded = irda_decode(instance->irda_decoder, level, duration);
+    if (message_decoded) {
+        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);
+    } else {
+        /* Skip first timing if it's starts from Space */
+        if ((instance->signal.timings_cnt == 0) && !level) {
+            return;
+        }
+
+        if (instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) {
+            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;
+        }
+    }
+}
+
+static int32_t irda_worker_thread_callback(void* context) {
+    IrdaWorker* instance = context;
+    uint32_t notify_value = 0;
+    LevelDuration level_duration;
+    TickType_t last_blink_time = 0;
+
+    while(1) {
+        BaseType_t result;
+        result = xTaskNotifyWait(pdFALSE, ULONG_MAX, &notify_value, 1000);
+        if (result != pdPASS)
+            continue;
+
+        if (notify_value & IRDA_WORKER_RX_RECEIVED) {
+            if (!instance->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) {
+                    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) {
+            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) {
+                printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT);
+                instance->overrun = false;
+                if (instance->blink_enable)
+                    notification_message(instance->notification, &sequence_reset_red);
+            } else {
+                irda_worker_process_timeout(instance);
+            }
+            instance->signal.timings_cnt = 0;
+        }
+        if (notify_value & IRDA_WORKER_EXIT)
+            break;
+    }
+
+    return 0;
+}
+
+void irda_worker_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback) {
+    furi_assert(instance);
+    instance->received_signal_callback = callback;
+}
+
+IrdaWorker* irda_worker_alloc() {
+    IrdaWorker* instance = furi_alloc(sizeof(IrdaWorker));
+
+    instance->thread = furi_thread_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));
+
+    instance->irda_decoder = irda_alloc_decoder();
+    instance->blink_enable = false;
+    instance->notification = furi_record_open("notification");
+
+    return instance;
+}
+
+void irda_worker_free(IrdaWorker* instance) {
+    furi_assert(instance);
+    furi_assert(!instance->worker_handle);
+
+    furi_record_close("notification");
+    irda_free_decoder(instance->irda_decoder);
+    vStreamBufferDelete(instance->stream);
+    furi_thread_free(instance->thread);
+
+    free(instance);
+}
+
+void irda_worker_set_context(IrdaWorker* instance, void* context) {
+    furi_assert(instance);
+    instance->context = context;
+}
+
+void irda_worker_start(IrdaWorker* instance) {
+    furi_assert(instance);
+    furi_assert(!instance->worker_handle);
+
+    furi_thread_start(instance->thread);
+
+    instance->worker_handle = furi_thread_get_thread_id(instance->thread);
+    api_hal_irda_rx_irq_init();
+    api_hal_irda_rx_timeout_irq_init(IRDA_WORKER_RX_TIMEOUT);
+    api_hal_irda_rx_irq_set_callback(irda_worker_rx_callback, instance);
+    api_hal_irda_rx_timeout_irq_set_callback(irda_worker_rx_timeout_callback, instance);
+}
+
+void irda_worker_stop(IrdaWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->worker_handle);
+
+    api_hal_irda_rx_timeout_irq_set_callback(NULL, NULL);
+    api_hal_irda_rx_irq_set_callback(NULL, NULL);
+    api_hal_irda_rx_irq_deinit();
+
+    xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits);
+
+    instance->worker_handle = NULL;
+
+    furi_thread_join(instance->thread);
+}
+
+bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal) {
+    furi_assert(signal);
+    return signal->decoded;
+}
+
+void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** timings, size_t* timings_cnt) {
+    furi_assert(signal);
+    furi_assert(timings);
+    furi_assert(timings_cnt);
+
+    *timings = signal->data.timings;
+    *timings_cnt = signal->timings_cnt;
+}
+
+const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal) {
+    furi_assert(signal);
+    return &signal->data.message;
+}
+
+void irda_worker_enable_blink_on_receiving(IrdaWorker* instance, bool enable) {
+    furi_assert(instance);
+    instance->blink_enable = enable;
+}
+

+ 91 - 0
lib/irda/worker/irda_worker.h

@@ -0,0 +1,91 @@
+#pragma once
+
+#include <irda.h>
+#include <api-hal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Interface struct of irda worker */
+typedef struct IrdaWorker IrdaWorker;
+/** Interface struct of received signal */
+typedef struct IrdaWorkerSignal IrdaWorkerSignal;
+
+/** Callback type to call by IrdaWorker thread when new signal is received */
+typedef void (*IrdaWorkerReceivedSignalCallback)(void* context, IrdaWorkerSignal* received_signal);
+
+/** Allocate IrdaWorker
+ *
+ * @return just created instance of IrdaWorker
+ */
+IrdaWorker* irda_worker_alloc();
+
+/** Free IrdaWorker
+ *
+ * @param[in]   instance - 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 api-hal, prepare all work.
+ *
+ * @param[in]   instance - IrdaWorker instance
+ */
+void irda_worker_start(IrdaWorker* instance);
+
+/** Stop IrdaWorker thread, deinitialize api-hal.
+ *
+ * @param[in]   instance - IrdaWorker instance
+ */
+void irda_worker_stop(IrdaWorker* instance);
+
+/** Clarify is received signal either decoded or raw
+ *
+ * @param[in]   signal - received signal
+ * @return      true if signal is decoded, false if signal is raw
+ */
+bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal);
+
+/** Acquire raw signal from interface struct 'IrdaWorkerSignal'.
+ * First, you have to ensure that signal is raw.
+ *
+ * @param[in]   signal - received signal
+ * @param[out]  timings - pointer to array of timings
+ * @param[out]  timings_cnt - pointer to amount of timings
+ */
+void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** timings, size_t* timings_cnt);
+
+/** Acquire decoded message from interface struct 'IrdaWorkerSignal'.
+ * First, you have to ensure that signal is decoded.
+ *
+ * @param[in]   signal - received signal
+ * @return      decoded irda message
+ */
+const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal);
+
+/** 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_enable_blink_on_receiving(IrdaWorker* instance, bool enable);
+
+#ifdef __cplusplus
+}
+#endif
+

+ 6 - 4
lib/lib.mk

@@ -94,9 +94,11 @@ CFLAGS			+= -I$(LIB_DIR)/file_reader
 CPP_SOURCES		+= $(wildcard $(LIB_DIR)/file_reader/*.cpp)
 CPP_SOURCES		+= $(wildcard $(LIB_DIR)/file_reader/*.cpp)
 
 
 #irda lib
 #irda lib
-CFLAGS			+= -I$(LIB_DIR)/irda
-C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*.c)
-C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*/*.c)
+CFLAGS			+= -I$(LIB_DIR)/irda/encoder_decoder
+CFLAGS			+= -I$(LIB_DIR)/irda/worker
+C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/encoder_decoder/*.c)
+C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/encoder_decoder/*/*.c)
+C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/worker/*.c)
 
 
 #args lib
 #args lib
 CFLAGS			+= -I$(LIB_DIR)/args
 CFLAGS			+= -I$(LIB_DIR)/args
@@ -110,4 +112,4 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/fl_subghz/*/*.c)
 CFLAGS			+= -I$(LIB_DIR)/app-scened-template
 CFLAGS			+= -I$(LIB_DIR)/app-scened-template
 C_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.c)
 C_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.c)
 CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.cpp)
 CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.cpp)
-CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp)
+CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp)