diff --git a/applications/irda/cli/irda-cli.cpp b/applications/irda/cli/irda-cli.cpp index 6a95e1d6eb9..fb5be8fd95e 100644 --- a/applications/irda/cli/irda-cli.cpp +++ b/applications/irda/cli/irda-cli.cpp @@ -1,24 +1,48 @@ -#include "app-template.h" -#include "cli/cli.h" -#include "cmsis_os2.h" +#include +#include +#include +#include +#include +#include #include #include -#include "irda.h" #include #include #include +#include -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."); 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"); 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) { @@ -63,38 +76,93 @@ static void irda_cli_print_usage(void) { 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 address = 0; 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) { - printf("Wrong arguments.\r\n"); - irda_cli_print_usage(); - return; + return false; } IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name); 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; } - 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() { diff --git a/applications/irda/irda-app-brute-force.cpp b/applications/irda/irda-app-brute-force.cpp index 4866a524f1f..21491e76eb2 100644 --- a/applications/irda/irda-app-brute-force.cpp +++ b/applications/irda/irda-app-brute-force.cpp @@ -16,7 +16,7 @@ bool IrdaAppBruteForce::calculate_messages() { file_parser.reset(); while(1) { - auto message = file_parser.read_message(&file); + auto message = file_parser.read_signal(&file); if(!message) break; auto element = records.find(message->name); if(element != records.cend()) { @@ -37,19 +37,19 @@ void IrdaAppBruteForce::stop_bruteforce() { } // 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()); - std::unique_ptr message; + std::unique_ptr file_signal; 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) { diff --git a/applications/irda/irda-app-brute-force.hpp b/applications/irda/irda-app-brute-force.hpp index 09a5002ed45..62b8f34e5a0 100644 --- a/applications/irda/irda-app-brute-force.hpp +++ b/applications/irda/irda-app-brute-force.hpp @@ -2,7 +2,6 @@ #include "furi/check.h" #include #include "irda-app-file-parser.hpp" -#include "irda-app-transceiver.hpp" class IrdaAppBruteForce { @@ -26,7 +25,7 @@ class IrdaAppBruteForce { public: bool calculate_messages(); void stop_bruteforce(); - bool send_next_bruteforce(const IrdaAppSignalTransceiver& receiver); + bool send_next_bruteforce(); bool start_bruteforce(int index, int& record_amount); void add_record(int index, const char* name); diff --git a/applications/irda/irda-app-file-parser.cpp b/applications/irda/irda-app-file-parser.cpp index 07c104b8a45..e0e274e54b1 100644 --- a/applications/irda/irda-app-file-parser.cpp +++ b/applications/irda/irda-app-file-parser.cpp @@ -1,26 +1,83 @@ #include "irda-app-file-parser.hpp" +#include "irda-app-remote-manager.hpp" +#include "irda-app-signal.h" +#include +#include +#include +#include +#include -std::unique_ptr IrdaAppFileParser::read_message(File* file) { +uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100); + +std::unique_ptr IrdaAppFileParser::read_signal(File* file) { while(1) { auto str = getline(file); 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; } } -std::unique_ptr - 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::parse_signal(const std::string& str) const { char protocol_name[32]; uint32_t address; uint32_t command; - auto irda_file_message = std::make_unique(); + auto irda_file_signal = std::make_unique(); int parsed = std::sscanf( str.c_str(), "%31s %31s A:%lX C:%lX", - irda_file_message->name, + irda_file_signal->name, protocol_name, &address, &command); @@ -47,12 +104,97 @@ std::unique_ptr return nullptr; } - irda_file_message->message = { + IrdaMessage message = { .protocol = protocol, .address = address, .command = command, .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::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(); + + 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; } diff --git a/applications/irda/irda-app-file-parser.hpp b/applications/irda/irda-app-file-parser.hpp index 55b2797187a..86a69a72f10 100644 --- a/applications/irda/irda-app-file-parser.hpp +++ b/applications/irda/irda-app-file-parser.hpp @@ -1,17 +1,26 @@ #pragma once -#include "file_reader/file_reader.hpp" -#include "irda.h" +#include +#include +#include "irda-app-remote-manager.hpp" class IrdaAppFileParser : public FileReader { public: typedef struct { char name[32]; - IrdaMessage message; - } IrdaFileMessage; + IrdaAppSignal signal; + } IrdaFileSignal; - std::unique_ptr read_message(File* file); + IrdaAppFileParser() { + /* Assume we can save max 512 samples */ + set_max_line_length(max_line_length); + } + + std::unique_ptr read_signal(File* file); + bool store_signal(File* file, const IrdaAppSignal& signal, const char* name); private: - std::unique_ptr parse_message(const std::string& str) const; + static const uint32_t max_line_length; + std::unique_ptr parse_signal(const std::string& str) const; + std::unique_ptr parse_signal_raw(const std::string& str) const; }; diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp index 4ae6c00c2d1..1eef6b4dfbc 100644 --- a/applications/irda/irda-app-remote-manager.cpp +++ b/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(); } bool IrdaAppRemoteManager::add_remote_with_button( const char* button_name, - const IrdaMessage* message) { + const IrdaAppSignal& signal) { furi_check(button_name != nullptr); - furi_check(message != nullptr); std::vector 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); remote = std::make_unique(new_name); - return add_button(button_name, message); + return add_button(button_name, signal); } IrdaAppRemote::IrdaAppRemote(const std::string& name) @@ -70,12 +69,12 @@ std::vector IrdaAppRemoteManager::get_button_list(void) const { 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); auto& buttons = remote->buttons; 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 { @@ -166,7 +165,6 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() { bool IrdaAppRemoteManager::store(void) { File file; - uint16_t write_count; std::string dirname(std::string("/") + irda_directory); IrdaAppFileParser file_parser; @@ -186,25 +184,9 @@ bool IrdaAppRemoteManager::store(void) { return false; } - char content[128]; - 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().context, "Cannot write\nto key file"); file_parser.get_fs_api().file.close(&file); @@ -267,9 +249,9 @@ bool IrdaAppRemoteManager::load(const std::string& name) { remote = std::make_unique(name); 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); diff --git a/applications/irda/irda-app-remote-manager.hpp b/applications/irda/irda-app-remote-manager.hpp index f7f068cd783..fda82e7e870 100644 --- a/applications/irda/irda-app-remote-manager.hpp +++ b/applications/irda/irda-app-remote-manager.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -6,14 +7,16 @@ #include #include #include +#include "irda-app-signal.h" + class IrdaAppRemoteButton { friend class IrdaAppRemoteManager; std::string name; - IrdaMessage message; + IrdaAppSignal signal; public: - IrdaAppRemoteButton(const char* name, const IrdaMessage* message) - : name(name), message (*message) {} + IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal) + : name(name), signal (signal) {} ~IrdaAppRemoteButton() {} }; @@ -38,8 +41,8 @@ class IrdaAppRemoteManager { std::string make_filename(const std::string& name) const; 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& strings); bool rename_button(uint32_t index, const char* str); @@ -50,7 +53,7 @@ class IrdaAppRemoteManager { std::string get_button_name(uint32_t index); std::string get_remote_name(); 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_remote(); diff --git a/applications/irda/irda-app-signal.cpp b/applications/irda/irda-app-signal.cpp new file mode 100644 index 00000000000..0a9a79567c7 --- /dev/null +++ b/applications/irda/irda-app-signal.cpp @@ -0,0 +1,95 @@ +#include "irda-app-signal.h" +#include + +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); + } +} diff --git a/applications/irda/irda-app-signal.h b/applications/irda/irda-app-signal.h new file mode 100644 index 00000000000..9701e2c1343 --- /dev/null +++ b/applications/irda/irda-app-signal.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include +#include + +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; + } +}; diff --git a/applications/irda/irda-app-transceiver.cpp b/applications/irda/irda-app-transceiver.cpp deleted file mode 100644 index c9f7d19ce2c..00000000000 --- a/applications/irda/irda-app-transceiver.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "irda-app.hpp" -#include "irda.h" -#include - -void IrdaAppSignalTransceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) { - IrdaAppEvent event; - const IrdaMessage* irda_message; - IrdaAppSignalTransceiver* this_ = static_cast(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); -} diff --git a/applications/irda/irda-app-transceiver.hpp b/applications/irda/irda-app-transceiver.hpp deleted file mode 100644 index e0c75b5b8da..00000000000 --- a/applications/irda/irda-app-transceiver.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -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; -}; - diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp index 4437ed9645b..6cd342ba44d 100644 --- a/applications/irda/irda-app.cpp +++ b/applications/irda/irda-app.cpp @@ -1,4 +1,5 @@ #include "irda-app.hpp" +#include #include #include #include @@ -99,10 +100,6 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() { return &remote_manager; } -IrdaAppSignalTransceiver* IrdaApp::get_transceiver() { - return &transceiver; -} - void IrdaApp::set_text_store(uint8_t index, const char* text...) { furi_check(index < text_store_max); @@ -220,3 +217,15 @@ void IrdaApp::notify_green_on() { void IrdaApp::notify_green_off() { 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; +} diff --git a/applications/irda/irda-app.hpp b/applications/irda/irda-app.hpp index 0e76fc8731c..233c4c9f6be 100644 --- a/applications/irda/irda-app.hpp +++ b/applications/irda/irda-app.hpp @@ -7,10 +7,10 @@ #include "scene/irda-app-scene.hpp" #include "irda-app-view-manager.hpp" #include "irda-app-remote-manager.hpp" -#include "irda-app-transceiver.hpp" #include #include #include +#include class IrdaApp { @@ -51,11 +51,15 @@ class IrdaApp { bool switch_to_previous_scene(uint8_t count = 1); Scene get_previous_scene(); IrdaAppViewManager* get_view_manager(); - IrdaAppSignalTransceiver* get_transceiver(); void set_text_store(uint8_t index, const char* text...); char* get_text_store(uint8_t index); uint8_t get_text_store_size(); IrdaAppRemoteManager* get_remote_manager(); + + 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& scenes_list); void set_edit_element(EditElement value); @@ -87,8 +91,10 @@ class IrdaApp { IrdaApp() { notification = static_cast(furi_record_open("notification")); + irda_worker = irda_worker_alloc(); } ~IrdaApp() { + irda_worker_free(irda_worker); furi_record_close("notification"); for (auto &it : scenes) delete it.second; @@ -103,9 +109,10 @@ class IrdaApp { uint32_t current_button; NotificationApp* notification; - IrdaAppSignalTransceiver transceiver; IrdaAppViewManager view_manager; IrdaAppRemoteManager remote_manager; + IrdaWorker* irda_worker; + IrdaAppSignal received_signal; std::forward_list previous_scenes_list; Scene current_scene = Scene::Start; diff --git a/applications/irda/irda_app_old.c b/applications/irda/irda_app_old.c deleted file mode 100644 index abfc30d6e05..00000000000 --- a/applications/irda/irda_app_old.c +++ /dev/null @@ -1,434 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#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 - } - } -} diff --git a/applications/irda/scene/irda-app-scene-edit-delete.cpp b/applications/irda/scene/irda-app-scene-edit-delete.cpp index da817cba002..c16ee7f25bb 100644 --- a/applications/irda/scene/irda-app-scene-edit-delete.cpp +++ b/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(); 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); - 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 { dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter); app->set_text_store( diff --git a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp index 45cd5ebf3e4..be0b8d63674 100644 --- a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp +++ b/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(); 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_result_callback( @@ -31,14 +36,13 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { if(event->type == IrdaAppEvent::Type::TextEditDone) { auto remote_manager = app->get_remote_manager(); - auto transceiver = app->get_transceiver(); bool result = false; if(app->get_learn_new_remote()) { 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 { - 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) { diff --git a/applications/irda/scene/irda-app-scene-learn-success.cpp b/applications/irda/scene/irda-app-scene-learn-success.cpp index 8b20f06530c..74e8db743fa 100644 --- a/applications/irda/scene/irda-app-scene-learn-success.cpp +++ b/applications/irda/scene/irda-app-scene-learn-success.cpp @@ -17,19 +17,26 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { 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_right_button_text(dialog_ex, "Save"); dialog_ex_set_center_button_text(dialog_ex, "Send"); @@ -50,9 +57,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { break; case DialogExResultCenter: { 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; } case DialogExResultRight: diff --git a/applications/irda/scene/irda-app-scene-learn.cpp b/applications/irda/scene/irda-app-scene-learn.cpp index 890a523ffd8..7fdd8bf7d0b 100644 --- a/applications/irda/scene/irda-app-scene-learn.cpp +++ b/applications/irda/scene/irda-app-scene-learn.cpp @@ -1,14 +1,40 @@ #include "../irda-app.hpp" +#include "../irda-app-event.hpp" +#include -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(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 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_text( 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 consumed = false; - if(event->type == IrdaAppEvent::Type::Tick) { + switch(event->type) { + case IrdaAppEvent::Type::Tick: consumed = true; app->notify_red_blink(); - } - if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { + break; + case IrdaAppEvent::Type::IrdaMessageReceived: app->notify_success(); app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); + irda_worker_stop(app->get_irda_worker()); + break; + case IrdaAppEvent::Type::Back: + consumed = true; + irda_worker_stop(app->get_irda_worker()); + app->switch_to_previous_scene(); + break; + default: + furi_assert(0); } return consumed; } void IrdaAppSceneLearn::on_exit(IrdaApp* app) { - auto transceiver = app->get_transceiver(); - transceiver->capture_stop(); } diff --git a/applications/irda/scene/irda-app-scene-remote.cpp b/applications/irda/scene/irda-app-scene-remote.cpp index dd1036f64d4..9260babecbd 100644 --- a/applications/irda/scene/irda-app-scene-remote.cpp +++ b/applications/irda/scene/irda-app-scene-remote.cpp @@ -64,8 +64,8 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { default: app->notify_click_and_blink(); 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; } } else if(event->type == IrdaAppEvent::Type::Back) { diff --git a/applications/irda/scene/irda-app-scene-universal-common.cpp b/applications/irda/scene/irda-app-scene-universal-common.cpp index 2eebe64ff6e..53c8a246f4b 100644 --- a/applications/irda/scene/irda-app-scene-universal-common.cpp +++ b/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(); IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; view_manager->send_event(&tick_event); - if(brute_force.send_next_bruteforce(*app->get_transceiver())) { + if(brute_force.send_next_bruteforce()) { progress_popup(app); } else { brute_force.stop_bruteforce(); diff --git a/applications/irda_monitor/irda_monitor.c b/applications/irda_monitor/irda_monitor.c index 37a64a7d865..56a81a3f72f 100644 --- a/applications/irda_monitor/irda_monitor.c +++ b/applications/irda_monitor/irda_monitor.c @@ -1,10 +1,11 @@ -#include "gui/canvas.h" -#include "irda.h" +#include +#include +#include +#include #include #include #include #include -#include #include #include #include @@ -20,25 +21,13 @@ typedef struct { } IrdaDelaysArray; typedef struct { - IrdaDecoderHandler* handler; char display_text[64]; osMessageQueueId_t event_queue; IrdaDelaysArray delays; + IrdaWorker* worker; + ViewPort* view_port; } 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) { furi_assert(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) { (void)p; - uint32_t counter = 0; - uint32_t print_counter = 0; IrdaMonitor* irda_monitor = furi_alloc(sizeof(IrdaMonitor)); irda_monitor->display_text[0] = 0; 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"); - 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) { InputEvent event; @@ -92,75 +124,14 @@ int32_t irda_monitor_app(void* p) { 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); - 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"); free(irda_monitor); diff --git a/firmware/targets/api-hal-include/api-hal-irda.h b/firmware/targets/api-hal-include/api-hal-irda.h index b53dccd04cb..45542a7c4e5 100644 --- a/firmware/targets/api-hal-include/api-hal-irda.h +++ b/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. * - * @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. @@ -27,20 +34,37 @@ void api_hal_irda_rx_irq_init(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. * - * @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 * 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); diff --git a/firmware/targets/f5/api-hal/api-hal-irda.c b/firmware/targets/f5/api-hal/api-hal-irda.c index 434c7037b93..c4861945f17 100644 --- a/firmware/targets/f5/api-hal/api-hal-irda.c +++ b/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 +#include + +#include #include #include + #include #include -#include "main.h" -#include "api-hal-pwm.h" - +#include +#include static struct{ - TimerISRCallback callback; - void *ctx; + ApiHalIrdaCaptureCallback capture_callback; + void *capture_context; + ApiHalIrdaTimeoutCallback timeout_callback; + void *timeout_context; } 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_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.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 0xFFFFFFFF; + TIM_InitStruct.Autoreload = 0x7FFFFFFE; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(TIM2, &TIM_InitStruct); + 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_DisableMasterSlaveMode(TIM2); + LL_TIM_EnableMasterSlaveMode(TIM2); 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_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_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_CC2(TIM2); @@ -90,22 +66,30 @@ void api_hal_irda_rx_irq_init(void) NVIC_EnableIRQ(TIM2_IRQn); } +/* Doesn't work. F5 deprecated. */ 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) { 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) { @@ -115,4 +99,3 @@ void api_hal_irda_pwm_set(float value, float freq) { void api_hal_irda_pwm_stop() { hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); } - diff --git a/firmware/targets/f5/api-hal/api-hal-resources.c b/firmware/targets/f5/api-hal/api-hal-resources.c index 3d0381518c9..76ccd3982b6 100644 --- a/firmware/targets/f5/api-hal/api-hal-resources.c +++ b/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_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}; \ No newline at end of file +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}; + diff --git a/firmware/targets/f5/api-hal/api-hal-resources.h b/firmware/targets/f5/api-hal/api-hal-resources.h index e288e061fa6..a927859e373 100644 --- a/firmware/targets/f5/api-hal/api-hal-resources.h +++ b/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_data_in; +extern const GpioPin gpio_irda_rx; +extern const GpioPin gpio_irda_tx; + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f5/api-hal/api-hal-tim.c b/firmware/targets/f5/api-hal/api-hal-tim.c index 2352e898041..fe06d213875 100644 --- a/firmware/targets/f5/api-hal/api-hal-tim.c +++ b/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)) { // input capture - api_hal_irda_tim_isr(TimerIRQSourceCCI1); consumed = true; } else { @@ -30,7 +29,6 @@ void TIM2_IRQHandler(void) if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { // input capture - api_hal_irda_tim_isr(TimerIRQSourceCCI2); consumed = true; } else { diff --git a/firmware/targets/f6/api-hal/api-hal-irda.c b/firmware/targets/f6/api-hal/api-hal-irda.c index 296171fcac6..332496df5f8 100644 --- a/firmware/targets/f6/api-hal/api-hal-irda.c +++ b/firmware/targets/f6/api-hal/api-hal-irda.c @@ -1,17 +1,22 @@ -#include "api-hal-interrupt.h" #include "api-hal-irda.h" +#include +#include +#include +#include #include #include #include #include -#include "main.h" -#include "api-hal-pwm.h" +#include +#include static struct{ - TimerISRCallback callback; - void *ctx; + ApiHalIrdaCaptureCallback capture_callback; + void *capture_context; + ApiHalIrdaTimeoutCallback timeout_callback; + void *timeout_context; } timer_irda; typedef enum{ @@ -19,107 +24,105 @@ typedef enum{ TimerIRQSourceCCI2, } 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; bool level = 0; switch (source) { 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; case TimerIRQSourceCCI2: - duration = LL_TIM_OC_GetCompareCH2(TIM2); - LL_TIM_SetCounter(TIM2, 0); - level = 1; + duration = LL_TIM_IC_GetCaptureCH2(TIM2); + level = 0; break; default: 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() { - 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); if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { // input capture 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); if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { // input capture 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_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.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 0xFFFFFFFF; + TIM_InitStruct.Autoreload = 0x7FFFFFFE; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(TIM2, &TIM_InitStruct); + 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_DisableMasterSlaveMode(TIM2); + LL_TIM_EnableMasterSlaveMode(TIM2); 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_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_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_CC2(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); 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_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) { @@ -127,15 +130,25 @@ void api_hal_irda_rx_irq_deinit(void) { 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) { 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) { diff --git a/firmware/targets/f6/api-hal/api-hal-resources.c b/firmware/targets/f6/api-hal/api-hal-resources.c index 6787e39dd10..0c8aeac9286 100644 --- a/firmware/targets/f6/api-hal/api-hal-resources.c +++ b/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_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}; \ No newline at end of file +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}; + diff --git a/firmware/targets/f6/api-hal/api-hal-resources.h b/firmware/targets/f6/api-hal/api-hal-resources.h index 8df8c0d0860..f3fd51ac0ea 100644 --- a/firmware/targets/f6/api-hal/api-hal-resources.h +++ b/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_data_in; +extern const GpioPin gpio_irda_rx; +extern const GpioPin gpio_irda_tx; + #ifdef __cplusplus } #endif diff --git a/lib/file_reader/file_reader.cpp b/lib/file_reader/file_reader.cpp index 0e4642f607b..3dc030e445c 100644 --- a/lib/file_reader/file_reader.cpp +++ b/lib/file_reader/file_reader.cpp @@ -1,9 +1,10 @@ -#include "file_reader/file_reader.hpp" +#include std::string FileReader::getline(File* file) { std::string str; size_t newline_index = 0; bool found_eol = false; + bool max_length_exceeded = false; while(1) { if(file_buf_cnt > 0) { @@ -20,7 +21,12 @@ std::string FileReader::getline(File* file) { 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); file_buf_cnt = file_buf_cnt - end_index; if(found_eol) break; @@ -33,6 +39,9 @@ std::string FileReader::getline(File* file) { } } + if (max_length_exceeded) + str.clear(); + return str; } diff --git a/lib/file_reader/file_reader.hpp b/lib/file_reader/file_reader.h similarity index 86% rename from lib/file_reader/file_reader.hpp rename to lib/file_reader/file_reader.h index e88a47faba0..f0acc442902 100644 --- a/lib/file_reader/file_reader.hpp +++ b/lib/file_reader/file_reader.h @@ -8,6 +8,7 @@ class FileReader { private: char file_buf[48]; size_t file_buf_cnt = 0; + size_t max_line_length = 0; SdCard_Api* sd_ex_api; FS_Api* fs_api; @@ -35,5 +36,9 @@ class FileReader { FS_Api& get_fs_api() { return *fs_api; } + + void set_max_line_length(size_t value) { + max_line_length = value; + } }; diff --git a/lib/irda/irda.c b/lib/irda/encoder_decoder/irda.c similarity index 100% rename from lib/irda/irda.c rename to lib/irda/encoder_decoder/irda.c diff --git a/lib/irda/irda.h b/lib/irda/encoder_decoder/irda.h similarity index 86% rename from lib/irda/irda.h rename to lib/irda/encoder_decoder/irda.h index 7bb34fb00e5..3ca669428ee 100644 --- a/lib/irda/irda.h +++ b/lib/irda/encoder_decoder/irda.h @@ -69,14 +69,6 @@ void irda_free_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. * @@ -117,19 +109,6 @@ uint8_t irda_get_protocol_command_length(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. * diff --git a/lib/irda/irda_common_decoder.c b/lib/irda/encoder_decoder/irda_common_decoder.c similarity index 100% rename from lib/irda/irda_common_decoder.c rename to lib/irda/encoder_decoder/irda_common_decoder.c diff --git a/lib/irda/irda_common_encoder.c b/lib/irda/encoder_decoder/irda_common_encoder.c similarity index 100% rename from lib/irda/irda_common_encoder.c rename to lib/irda/encoder_decoder/irda_common_encoder.c diff --git a/lib/irda/irda_common_i.h b/lib/irda/encoder_decoder/irda_common_i.h similarity index 100% rename from lib/irda/irda_common_i.h rename to lib/irda/encoder_decoder/irda_common_i.h diff --git a/lib/irda/irda_common_protocol_defs.c b/lib/irda/encoder_decoder/irda_common_protocol_defs.c similarity index 100% rename from lib/irda/irda_common_protocol_defs.c rename to lib/irda/encoder_decoder/irda_common_protocol_defs.c diff --git a/lib/irda/irda_i.h b/lib/irda/encoder_decoder/irda_i.h similarity index 100% rename from lib/irda/irda_i.h rename to lib/irda/encoder_decoder/irda_i.h diff --git a/lib/irda/irda_protocol_defs_i.h b/lib/irda/encoder_decoder/irda_protocol_defs_i.h similarity index 100% rename from lib/irda/irda_protocol_defs_i.h rename to lib/irda/encoder_decoder/irda_protocol_defs_i.h diff --git a/lib/irda/nec/irda_decoder_nec.c b/lib/irda/encoder_decoder/nec/irda_decoder_nec.c similarity index 100% rename from lib/irda/nec/irda_decoder_nec.c rename to lib/irda/encoder_decoder/nec/irda_decoder_nec.c diff --git a/lib/irda/nec/irda_encoder_nec.c b/lib/irda/encoder_decoder/nec/irda_encoder_nec.c similarity index 100% rename from lib/irda/nec/irda_encoder_nec.c rename to lib/irda/encoder_decoder/nec/irda_encoder_nec.c diff --git a/lib/irda/rc6/irda_decoder_rc6.c b/lib/irda/encoder_decoder/rc6/irda_decoder_rc6.c similarity index 100% rename from lib/irda/rc6/irda_decoder_rc6.c rename to lib/irda/encoder_decoder/rc6/irda_decoder_rc6.c diff --git a/lib/irda/rc6/irda_encoder_rc6.c b/lib/irda/encoder_decoder/rc6/irda_encoder_rc6.c similarity index 100% rename from lib/irda/rc6/irda_encoder_rc6.c rename to lib/irda/encoder_decoder/rc6/irda_encoder_rc6.c diff --git a/lib/irda/samsung/irda_decoder_samsung.c b/lib/irda/encoder_decoder/samsung/irda_decoder_samsung.c similarity index 100% rename from lib/irda/samsung/irda_decoder_samsung.c rename to lib/irda/encoder_decoder/samsung/irda_decoder_samsung.c diff --git a/lib/irda/samsung/irda_encoder_samsung.c b/lib/irda/encoder_decoder/samsung/irda_encoder_samsung.c similarity index 100% rename from lib/irda/samsung/irda_encoder_samsung.c rename to lib/irda/encoder_decoder/samsung/irda_encoder_samsung.c diff --git a/lib/irda/irda_transmit.c b/lib/irda/worker/irda_transmit.c similarity index 65% rename from lib/irda/irda_transmit.c rename to lib/irda/worker/irda_transmit.c index b6b00bf5742..ae4d0f4f480 100644 --- a/lib/irda/irda_transmit.c +++ b/lib/irda/worker/irda_transmit.c @@ -6,9 +6,11 @@ #include #include -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) { - api_hal_irda_pwm_set(IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY); + api_hal_irda_pwm_set(duty_cycle, frequency); delay_us(duration); } else { 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) { __disable_irq(); 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(); } @@ -49,7 +60,7 @@ void irda_send(const IrdaMessage* message, int times) { while (times) { status = irda_encode(handler, &duration, &level); if (status != IrdaStatusError) { - irda_set_tx(duration, level); + IRDA_SET_TX_COMMON(duration, level); } else { furi_assert(0); break; @@ -58,7 +69,7 @@ void irda_send(const IrdaMessage* message, int times) { --times; } - irda_set_tx(0, false); + IRDA_SET_TX_COMMON(0, false); __enable_irq(); irda_free_encoder(handler); diff --git a/lib/irda/worker/irda_transmit.h b/lib/irda/worker/irda_transmit.h new file mode 100644 index 00000000000..d540d5597bb --- /dev/null +++ b/lib/irda/worker/irda_transmit.h @@ -0,0 +1,41 @@ +#include +#include + +#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 + diff --git a/lib/irda/worker/irda_worker.c b/lib/irda/worker/irda_worker.c new file mode 100644 index 00000000000..8ea87fe006d --- /dev/null +++ b/lib/irda/worker/irda_worker.c @@ -0,0 +1,237 @@ +#include "irda_worker.h" +#include +#include +#include +#include +#include +#include +#include + +#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, ¬ify_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; +} + diff --git a/lib/irda/worker/irda_worker.h b/lib/irda/worker/irda_worker.h new file mode 100644 index 00000000000..caac873d37f --- /dev/null +++ b/lib/irda/worker/irda_worker.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include + +#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 + diff --git a/lib/lib.mk b/lib/lib.mk index 7686a9bbe6f..882923896f8 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -94,9 +94,11 @@ CFLAGS += -I$(LIB_DIR)/file_reader CPP_SOURCES += $(wildcard $(LIB_DIR)/file_reader/*.cpp) #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 CFLAGS += -I$(LIB_DIR)/args @@ -110,4 +112,4 @@ C_SOURCES += $(wildcard $(LIB_DIR)/fl_subghz/*/*.c) CFLAGS += -I$(LIB_DIR)/app-scened-template 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) \ No newline at end of file +CPP_SOURCES += $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp)