[FL-1505] Add RAW format (#576)
* Add RAW format * F5 stubs for build to pass * Fix saving decoded signal error * Irda: set ISR before starting timer, remove explicit NVIC configuration Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									a2dfa33a9f
								
							
						
					
					
						commit
						13c5a8cb20
					
				| @ -1,24 +1,48 @@ | ||||
| #include "app-template.h" | ||||
| #include "cli/cli.h" | ||||
| #include "cmsis_os2.h" | ||||
| #include <api-hal-delay.h> | ||||
| #include <irda.h> | ||||
| #include <app-template.h> | ||||
| #include <cli/cli.h> | ||||
| #include <cmsis_os2.h> | ||||
| #include <irda_worker.h> | ||||
| #include <furi.h> | ||||
| #include <api-hal-irda.h> | ||||
| #include "irda.h" | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <m-string.h> | ||||
| #include <irda_transmit.h> | ||||
| 
 | ||||
| typedef struct IrdaCli { | ||||
|     IrdaDecoderHandler* handler; | ||||
|     osMessageQueueId_t message_queue; | ||||
| } IrdaCli; | ||||
| 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; | ||||
| 
 | ||||
| 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); | ||||
|     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 bool parse_message(const char* str, IrdaMessage* message) { | ||||
|     uint32_t command = 0; | ||||
|     uint32_t address = 0; | ||||
|     char protocol_name[32]; | ||||
|     int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &address, &command); | ||||
| 
 | ||||
|     if(parsed != 3) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name); | ||||
| 
 | ||||
|     if(!irda_is_protocol_valid(protocol)) { | ||||
|         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; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|     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(parsed != 3) { | ||||
|     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(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     IrdaMessage message = { | ||||
|         .protocol = protocol, | ||||
|         .address = address, | ||||
|         .command = command, | ||||
|         .repeat = false, | ||||
|     }; | ||||
|     irda_send(&message, 1); | ||||
|     free(timings); | ||||
| } | ||||
| 
 | ||||
| extern "C" void irda_cli_init() { | ||||
|  | ||||
| @ -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<IrdaAppFileParser::IrdaFileMessage> message; | ||||
|     std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> 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) { | ||||
|  | ||||
| @ -2,7 +2,6 @@ | ||||
| #include "furi/check.h" | ||||
| #include <unordered_map> | ||||
| #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); | ||||
| 
 | ||||
|  | ||||
| @ -1,26 +1,83 @@ | ||||
| #include "irda-app-file-parser.hpp" | ||||
| #include "irda-app-remote-manager.hpp" | ||||
| #include "irda-app-signal.h" | ||||
| #include <irda.h> | ||||
| #include <cstdio> | ||||
| #include <stdint.h> | ||||
| #include <string_view> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> IrdaAppFileParser::read_message(File* file) { | ||||
| uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100); | ||||
| 
 | ||||
| std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> IrdaAppFileParser::read_signal(File* file) { | ||||
|     while(1) { | ||||
|         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::IrdaFileMessage> | ||||
|     IrdaAppFileParser::parse_message(const std::string& str) const { | ||||
| bool IrdaAppFileParser::store_signal(File* file, const IrdaAppSignal& signal, const char* name) { | ||||
|     char* content = new char[max_line_length]; | ||||
|     size_t written = 0; | ||||
| 
 | ||||
|     if(!signal.is_raw()) { | ||||
|         auto message = signal.get_message(); | ||||
|         auto protocol = message.protocol; | ||||
| 
 | ||||
|         sniprintf( | ||||
|             content, | ||||
|             max_line_length, | ||||
|             "%.31s %.31s A:%0*lX C:%0*lX\n", | ||||
|             name, | ||||
|             irda_get_protocol_name(protocol), | ||||
|             irda_get_protocol_address_length(protocol), | ||||
|             message.address, | ||||
|             irda_get_protocol_command_length(protocol), | ||||
|             message.command); | ||||
|         written = strlen(content); | ||||
|     } else { | ||||
|         int duty_cycle = 100 * IRDA_COMMON_DUTY_CYCLE; | ||||
|         written += sniprintf( | ||||
|             &content[written], | ||||
|             max_line_length - written, | ||||
|             "%.31s RAW F:%d DC:%d", | ||||
|             name, | ||||
|             IRDA_COMMON_CARRIER_FREQUENCY, | ||||
|             duty_cycle); | ||||
| 
 | ||||
|         auto& raw_signal = signal.get_raw_signal(); | ||||
|         for(size_t i = 0; i < raw_signal.timings_cnt; ++i) { | ||||
|             written += sniprintf( | ||||
|                 &content[written], max_line_length - written, " %ld", raw_signal.timings[i]); | ||||
|             furi_assert(written <= max_line_length); | ||||
|         } | ||||
|         written += snprintf(&content[written], max_line_length - written, "\n"); | ||||
|     } | ||||
|     furi_assert(written < max_line_length); | ||||
| 
 | ||||
|     size_t write_count = 0; | ||||
|     write_count = get_fs_api().file.write(file, content, written); | ||||
|     delete[] content; | ||||
|     return (file->error_id == FSE_OK) && (write_count == written); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> | ||||
|     IrdaAppFileParser::parse_signal(const std::string& str) const { | ||||
|     char protocol_name[32]; | ||||
|     uint32_t address; | ||||
|     uint32_t command; | ||||
|     auto irda_file_message = std::make_unique<IrdaFileMessage>(); | ||||
|     auto irda_file_signal = std::make_unique<IrdaFileSignal>(); | ||||
| 
 | ||||
|     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<IrdaAppFileParser::IrdaFileMessage> | ||||
|         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::IrdaFileSignal> | ||||
|     IrdaAppFileParser::parse_signal_raw(const std::string& string) const { | ||||
|     char protocol_name[32]; | ||||
|     uint32_t frequency; | ||||
|     uint32_t duty_cycle; | ||||
|     int str_len = string.size(); | ||||
|     std::string_view str(string.c_str()); | ||||
|     auto irda_file_signal = std::make_unique<IrdaFileSignal>(); | ||||
| 
 | ||||
|     int parsed = std::sscanf( | ||||
|         str.data(), | ||||
|         "%31s %31s F:%ld DC:%ld", | ||||
|         irda_file_signal->name, | ||||
|         protocol_name, | ||||
|         &frequency, | ||||
|         &duty_cycle); | ||||
| 
 | ||||
|     if(parsed != 4) { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     char dummy[100] = {0}; | ||||
|     int header_len = 0; | ||||
|     header_len = sniprintf( | ||||
|         dummy, | ||||
|         sizeof(dummy), | ||||
|         "%.31s %.31s F:%ld DC:%ld", | ||||
|         irda_file_signal->name, | ||||
|         protocol_name, | ||||
|         frequency, | ||||
|         duty_cycle); | ||||
| 
 | ||||
|     furi_assert(header_len < str_len); | ||||
|     str.remove_prefix(header_len); | ||||
| 
 | ||||
|     /* move allocated timings into raw signal object */ | ||||
|     IrdaAppSignal::RawSignal raw_signal = {.timings_cnt = 0, .timings = new uint32_t[500]}; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     while(!str.empty()) { | ||||
|         char buf[10]; | ||||
|         size_t index = str.find_first_not_of(' ', 1); | ||||
|         if(index == std::string_view::npos) { | ||||
|             result = true; | ||||
|             break; | ||||
|         } | ||||
|         str.remove_prefix(index); | ||||
|         parsed = std::sscanf(str.data(), "%9s", buf); | ||||
|         if(parsed != 1) { | ||||
|             break; | ||||
|         } | ||||
|         str.remove_prefix(strlen(buf)); | ||||
| 
 | ||||
|         int value = atoi(buf); | ||||
|         if(value <= 0) { | ||||
|             break; | ||||
|         } | ||||
|         raw_signal.timings[raw_signal.timings_cnt] = value; | ||||
|         ++raw_signal.timings_cnt; | ||||
|         if(raw_signal.timings_cnt >= 500) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(result) { | ||||
|         irda_file_signal->signal.set_raw_signal(raw_signal.timings, raw_signal.timings_cnt); | ||||
|     } else { | ||||
|         (void)irda_file_signal.release(); | ||||
|         delete[] raw_signal.timings; | ||||
|     } | ||||
|     return irda_file_signal; | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,26 @@ | ||||
| #pragma once | ||||
| #include "file_reader/file_reader.hpp" | ||||
| #include "irda.h" | ||||
| #include <file_reader/file_reader.h> | ||||
| #include <irda.h> | ||||
| #include "irda-app-remote-manager.hpp" | ||||
| 
 | ||||
| class IrdaAppFileParser : public FileReader { | ||||
| public: | ||||
|     typedef struct { | ||||
|         char name[32]; | ||||
|         IrdaMessage message; | ||||
|     } IrdaFileMessage; | ||||
|         IrdaAppSignal signal; | ||||
|     } IrdaFileSignal; | ||||
| 
 | ||||
|     std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> read_message(File* file); | ||||
|     IrdaAppFileParser() { | ||||
|         /* Assume we can save max 512 samples */ | ||||
|         set_max_line_length(max_line_length); | ||||
|     } | ||||
| 
 | ||||
|     std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> read_signal(File* file); | ||||
|     bool store_signal(File* file, const IrdaAppSignal& signal, const char* name); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<IrdaFileMessage> parse_message(const std::string& str) const; | ||||
|     static const uint32_t max_line_length; | ||||
|     std::unique_ptr<IrdaFileSignal> parse_signal(const std::string& str) const; | ||||
|     std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -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<std::string> 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<IrdaAppRemote>(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<std::string> 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<IrdaAppRemote>(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); | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #pragma once | ||||
| #include <irda_worker.h> | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| @ -6,14 +7,16 @@ | ||||
| #include <irda.h> | ||||
| #include <sd-card-api.h> | ||||
| #include <filesystem-api.h> | ||||
| #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<std::string>& strings); | ||||
|     bool rename_button(uint32_t index, const char* str); | ||||
| @ -50,7 +53,7 @@ public: | ||||
|     std::string get_button_name(uint32_t index); | ||||
|     std::string get_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(); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										95
									
								
								applications/irda/irda-app-signal.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								applications/irda/irda-app-signal.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| #include "irda-app-signal.h" | ||||
| #include <irda_transmit.h> | ||||
| 
 | ||||
| void IrdaAppSignal::copy_timings(const uint32_t* timings, size_t size) { | ||||
|     furi_assert(size); | ||||
|     furi_assert(timings); | ||||
| 
 | ||||
|     if(size) { | ||||
|         payload.raw.timings = new uint32_t[size]; | ||||
|         payload.raw.timings_cnt = size; | ||||
|         memcpy(payload.raw.timings, timings, size * sizeof(uint32_t)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::clear_timings() { | ||||
|     if(!decoded) { | ||||
|         delete[] payload.raw.timings; | ||||
|         payload.raw.timings_cnt = 0; | ||||
|         payload.raw.timings = nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(const uint32_t* timings, size_t timings_cnt) { | ||||
|     decoded = false; | ||||
|     copy_timings(timings, timings_cnt); | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(const IrdaMessage* irda_message) { | ||||
|     decoded = true; | ||||
|     payload.message = *irda_message; | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal& IrdaAppSignal::operator=(const IrdaAppSignal& other) { | ||||
|     clear_timings(); | ||||
|     decoded = other.decoded; | ||||
|     if(decoded) { | ||||
|         payload.message = other.payload.message; | ||||
|     } else { | ||||
|         copy_timings(other.payload.raw.timings, other.payload.raw.timings_cnt); | ||||
|     } | ||||
| 
 | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) { | ||||
|     decoded = other.decoded; | ||||
|     if(decoded) { | ||||
|         payload.message = other.payload.message; | ||||
|     } else { | ||||
|         copy_timings(other.payload.raw.timings, other.payload.raw.timings_cnt); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) { | ||||
|     clear_timings(); | ||||
| 
 | ||||
|     decoded = other.decoded; | ||||
|     if(decoded) { | ||||
|         payload.message = other.payload.message; | ||||
|     } else { | ||||
|         furi_assert(other.payload.raw.timings_cnt > 0); | ||||
| 
 | ||||
|         payload.raw.timings = other.payload.raw.timings; | ||||
|         payload.raw.timings_cnt = other.payload.raw.timings_cnt; | ||||
|         other.payload.raw.timings = nullptr; | ||||
|         other.payload.raw.timings_cnt = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::set_message(const IrdaMessage* irda_message) { | ||||
|     clear_timings(); | ||||
|     decoded = true; | ||||
|     payload.message = *irda_message; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::set_raw_signal(uint32_t* timings, size_t timings_cnt) { | ||||
|     clear_timings(); | ||||
|     decoded = false; | ||||
|     payload.raw.timings = timings; | ||||
|     payload.raw.timings_cnt = timings_cnt; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::copy_raw_signal(uint32_t* timings, size_t timings_cnt) { | ||||
|     clear_timings(); | ||||
|     decoded = false; | ||||
|     copy_timings(timings, timings_cnt); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::transmit() const { | ||||
|     if(decoded) { | ||||
|         irda_send(&payload.message, 1); | ||||
|     } else { | ||||
|         irda_send_raw(payload.raw.timings, payload.raw.timings_cnt, true); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										61
									
								
								applications/irda/irda-app-signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								applications/irda/irda-app-signal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| #pragma once | ||||
| #include <irda_worker.h> | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <irda.h> | ||||
| 
 | ||||
| class IrdaAppSignal { | ||||
| public: | ||||
|     typedef struct { | ||||
|         size_t timings_cnt; | ||||
|         uint32_t* timings; | ||||
|     } RawSignal; | ||||
| 
 | ||||
| private: | ||||
|     bool decoded; | ||||
|     union { | ||||
|         IrdaMessage message; | ||||
|         RawSignal raw; | ||||
|     } payload; | ||||
| 
 | ||||
|     void copy_timings(const uint32_t* timings, size_t size); | ||||
|     void clear_timings(); | ||||
| 
 | ||||
| public: | ||||
|     IrdaAppSignal() { | ||||
|         decoded = true; | ||||
|         payload.message.protocol = IrdaProtocolUnknown; | ||||
|     } | ||||
| 
 | ||||
|     ~IrdaAppSignal() { | ||||
|         clear_timings(); | ||||
|     } | ||||
| 
 | ||||
|     IrdaAppSignal(const uint32_t* timings, size_t timings_cnt); | ||||
|     IrdaAppSignal(const IrdaMessage* irda_message); | ||||
| 
 | ||||
|     IrdaAppSignal(const IrdaAppSignal& other); | ||||
|     IrdaAppSignal(IrdaAppSignal&& other); | ||||
| 
 | ||||
|     IrdaAppSignal& operator=(const IrdaAppSignal& signal); | ||||
| 
 | ||||
|     void set_message(const IrdaMessage* irda_message); | ||||
|     void set_raw_signal(uint32_t* timings, size_t timings_cnt); | ||||
|     void copy_raw_signal(uint32_t* timings, size_t timings_cnt); | ||||
| 
 | ||||
|     void transmit() const; | ||||
| 
 | ||||
|     bool is_raw(void) const { | ||||
|         return !decoded; | ||||
|     } | ||||
| 
 | ||||
|     const IrdaMessage& get_message(void) const { | ||||
|         furi_assert(decoded); | ||||
|         return payload.message; | ||||
|     } | ||||
| 
 | ||||
|     const RawSignal& get_raw_signal(void) const { | ||||
|         furi_assert(!decoded); | ||||
|         return payload.raw; | ||||
|     } | ||||
| }; | ||||
| @ -1,56 +0,0 @@ | ||||
| #include "irda-app.hpp" | ||||
| #include "irda.h" | ||||
| #include <api-hal-irda.h> | ||||
| 
 | ||||
| void IrdaAppSignalTransceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) { | ||||
|     IrdaAppEvent event; | ||||
|     const IrdaMessage* irda_message; | ||||
|     IrdaAppSignalTransceiver* this_ = static_cast<IrdaAppSignalTransceiver*>(ctx); | ||||
| 
 | ||||
|     irda_message = irda_decode(this_->decoder, level, duration); | ||||
|     if(irda_message) { | ||||
|         this_->message = *irda_message; | ||||
|         event.type = IrdaAppEvent::Type::IrdaMessageReceived; | ||||
|         osStatus_t result = osMessageQueuePut(this_->event_queue, &event, 0, 0); | ||||
|         furi_check(result == osOK); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignalTransceiver::IrdaAppSignalTransceiver(void) | ||||
|     : capture_started(false) | ||||
|     , decoder(irda_alloc_decoder()) { | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignalTransceiver::~IrdaAppSignalTransceiver() { | ||||
|     capture_stop(); | ||||
|     irda_free_decoder(decoder); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignalTransceiver::capture_once_start(osMessageQueueId_t queue) { | ||||
|     event_queue = queue; | ||||
|     irda_reset_decoder(decoder); | ||||
|     if(!capture_started) { | ||||
|         capture_started = true; | ||||
|         api_hal_irda_rx_irq_set_callback(IrdaAppSignalTransceiver::irda_rx_callback, this); | ||||
|         api_hal_irda_rx_irq_init(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignalTransceiver::capture_stop(void) { | ||||
|     IrdaAppEvent event; | ||||
| 
 | ||||
|     if(capture_started) { | ||||
|         capture_started = false; | ||||
|         api_hal_irda_rx_irq_deinit(); | ||||
|         while(osMessageQueueGet(this->event_queue, &event, 0, 0) == osOK) | ||||
|             ; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaMessage* IrdaAppSignalTransceiver::get_last_message(void) { | ||||
|     return &message; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignalTransceiver::send_message(const IrdaMessage* message) const { | ||||
|     irda_send(message, 1); | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include <irda.h> | ||||
| 
 | ||||
| class IrdaAppSignalTransceiver { | ||||
| public: | ||||
|     IrdaAppSignalTransceiver(void); | ||||
|     ~IrdaAppSignalTransceiver(void); | ||||
|     void capture_once_start(osMessageQueueId_t event_queue); | ||||
|     void capture_stop(void); | ||||
|     IrdaMessage* get_last_message(void); | ||||
|     void send_message(const IrdaMessage* message) const; | ||||
| 
 | ||||
| private: | ||||
|     bool capture_started; | ||||
|     osMessageQueueId_t event_queue; | ||||
|     static void irda_rx_callback(void* ctx, bool level, uint32_t duration); | ||||
|     IrdaDecoderHandler* decoder; | ||||
|     IrdaMessage message; | ||||
| }; | ||||
| 
 | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "irda-app.hpp" | ||||
| #include <irda_worker.h> | ||||
| #include <furi.h> | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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 <forward_list> | ||||
| #include <stdint.h> | ||||
| #include <notification/notification-messages.h> | ||||
| #include <irda_worker.h> | ||||
| 
 | ||||
| 
 | ||||
| class IrdaApp { | ||||
| @ -51,11 +51,15 @@ public: | ||||
|     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<Scene>& scenes_list); | ||||
| 
 | ||||
|     void set_edit_element(EditElement value); | ||||
| @ -87,8 +91,10 @@ public: | ||||
| 
 | ||||
|     IrdaApp() { | ||||
|         notification = static_cast<NotificationApp*>(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 @@ private: | ||||
|     uint32_t current_button; | ||||
| 
 | ||||
|     NotificationApp* notification; | ||||
|     IrdaAppSignalTransceiver transceiver; | ||||
|     IrdaAppViewManager view_manager; | ||||
|     IrdaAppRemoteManager remote_manager; | ||||
|     IrdaWorker* irda_worker; | ||||
|     IrdaAppSignal received_signal; | ||||
| 
 | ||||
|     std::forward_list<Scene> previous_scenes_list; | ||||
|     Scene current_scene = Scene::Start; | ||||
|  | ||||
| @ -1,434 +0,0 @@ | ||||
| #include <furi.h> | ||||
| #include <api-hal.h> | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| #include <cli/cli.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| #include <api-hal-irda.h> | ||||
| #include "irda.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     EventTypeTick, | ||||
|     EventTypeKey, | ||||
|     EventTypeRX, | ||||
| } EventType; | ||||
| 
 | ||||
| typedef IrdaMessage IrDAPacket; | ||||
| 
 | ||||
| typedef struct { | ||||
|     union { | ||||
|         InputEvent input; | ||||
|         IrDAPacket rx; | ||||
|     } value; | ||||
|     EventType type; | ||||
| } AppEvent; | ||||
| 
 | ||||
| //typedef struct {
 | ||||
| //    IrdaProtocol protocol;
 | ||||
| //    uint32_t address;
 | ||||
| //    uint32_t command;
 | ||||
| //} IrDAPacket;
 | ||||
| 
 | ||||
| #define IRDA_PACKET_COUNT 8 | ||||
| 
 | ||||
| typedef struct { | ||||
|     osMessageQueueId_t cli_ir_rx_queue; | ||||
|     Cli* cli; | ||||
|     bool cli_cmd_is_active; | ||||
| } IrDAApp; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t mode_id; | ||||
|     uint16_t carrier_freq; | ||||
|     uint8_t carrier_duty_cycle_id; | ||||
| 
 | ||||
|     uint8_t packet_id; | ||||
|     IrDAPacket packets[IRDA_PACKET_COUNT]; | ||||
| } State; | ||||
| 
 | ||||
| typedef void (*ModeInput)(AppEvent*, State*); | ||||
| typedef void (*ModeRender)(Canvas*, State*); | ||||
| 
 | ||||
| void input_carrier(AppEvent* event, State* state); | ||||
| void render_carrier(Canvas* canvas, State* state); | ||||
| void input_packet(AppEvent* event, State* state); | ||||
| void render_packet(Canvas* canvas, State* state); | ||||
| 
 | ||||
| typedef struct { | ||||
|     ModeRender render; | ||||
|     ModeInput input; | ||||
| } Mode; | ||||
| 
 | ||||
| const Mode modes[] = { | ||||
|     {.render = render_carrier, .input = input_carrier}, | ||||
|     {.render = render_packet, .input = input_packet}, | ||||
| }; | ||||
| 
 | ||||
| const float duty_cycles[] = {0.1, 0.25, 0.333, 0.5, 1.0}; | ||||
| 
 | ||||
| void render_carrier(Canvas* canvas, State* state) { | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 2, 25, "carrier mode >"); | ||||
|     canvas_draw_str(canvas, 2, 37, "? /\\ freq | \\/ duty cycle"); | ||||
|     { | ||||
|         char buf[24]; | ||||
|         sprintf(buf, "frequency: %u Hz", state->carrier_freq); | ||||
|         canvas_draw_str(canvas, 2, 50, buf); | ||||
|         sprintf( | ||||
|             buf, "duty cycle: %d/1000", (int)(duty_cycles[state->carrier_duty_cycle_id] * 1000)); | ||||
|         canvas_draw_str(canvas, 2, 62, buf); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void input_carrier(AppEvent* event, State* state) { | ||||
|     if(event->value.input.key == InputKeyOk) { | ||||
|         if(event->value.input.type == InputTypePress) { | ||||
|             api_hal_irda_pwm_set(duty_cycles[state->carrier_duty_cycle_id], state->carrier_freq); | ||||
|         } else if(event->value.input.type == InputTypeRelease) { | ||||
|             api_hal_irda_pwm_stop(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { | ||||
|         if(state->carrier_freq < 45000) { | ||||
|             state->carrier_freq += 1000; | ||||
|         } else { | ||||
|             state->carrier_freq = 33000; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { | ||||
|         uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]); | ||||
|         if(state->carrier_duty_cycle_id < (duty_cycles_count - 1)) { | ||||
|             state->carrier_duty_cycle_id++; | ||||
|         } else { | ||||
|             state->carrier_duty_cycle_id = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void render_packet(Canvas* canvas, State* state) { | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 2, 25, "< packet mode"); | ||||
|     canvas_draw_str(canvas, 2, 37, "? /\\ \\/ packet"); | ||||
|     { | ||||
|         char buf[30]; | ||||
|         sprintf( | ||||
|             buf, | ||||
|             "P[%d]: %s 0x%lX 0x%lX", | ||||
|             state->packet_id, | ||||
|             irda_get_protocol_name(state->packets[state->packet_id].protocol), | ||||
|             state->packets[state->packet_id].address, | ||||
|             state->packets[state->packet_id].command); | ||||
|         canvas_draw_str(canvas, 2, 50, buf); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void input_packet(AppEvent* event, State* state) { | ||||
|     if(event->value.input.key == InputKeyOk) { | ||||
|         if(event->value.input.type == InputTypeShort) { | ||||
|             IrdaMessage message = { | ||||
|                 .protocol = state->packets[state->packet_id].protocol, | ||||
|                 .address = state->packets[state->packet_id].address, | ||||
|                 .command = state->packets[state->packet_id].command, | ||||
|             }; | ||||
|             irda_send(&message, 1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { | ||||
|         if(state->packet_id < (IRDA_PACKET_COUNT - 1)) { | ||||
|             state->packet_id++; | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { | ||||
|         if(state->packet_id > 0) { | ||||
|             state->packet_id--; | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void render_callback(Canvas* canvas, void* ctx) { | ||||
|     State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); | ||||
| 
 | ||||
|     if(state != NULL) { | ||||
|         canvas_clear(canvas); | ||||
|         canvas_set_color(canvas, ColorBlack); | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str(canvas, 2, 12, "irda test"); | ||||
| 
 | ||||
|         modes[state->mode_id].render(canvas, state); | ||||
| 
 | ||||
|         release_mutex((ValueMutex*)ctx, state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void input_callback(InputEvent* input_event, void* ctx) { | ||||
|     osMessageQueueId_t event_queue = ctx; | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     event.type = EventTypeKey; | ||||
|     event.value.input = *input_event; | ||||
|     osMessageQueuePut(event_queue, &event, 0, 0); | ||||
| } | ||||
| 
 | ||||
| void init_packet( | ||||
|     State* state, | ||||
|     uint8_t index, | ||||
|     IrdaProtocol protocol, | ||||
|     uint32_t address, | ||||
|     uint32_t command) { | ||||
|     if(index >= IRDA_PACKET_COUNT) return; | ||||
|     state->packets[index].protocol = protocol; | ||||
|     state->packets[index].address = address; | ||||
|     state->packets[index].command = command; | ||||
| } | ||||
| 
 | ||||
| void irda_cli_cmd_rx(Cli* cli, string_t args, void* context) { | ||||
|     furi_assert(context); | ||||
|     IrDAPacket packet; | ||||
|     IrDAApp* app = context; | ||||
|     app->cli_cmd_is_active = true; | ||||
|     bool exit = false; | ||||
| 
 | ||||
|     printf("Reading income packets...\r\nPress Ctrl+C to abort\r\n"); | ||||
|     while(!exit) { | ||||
|         exit = cli_cmd_interrupt_received(app->cli); | ||||
|         osStatus status = osMessageQueueGet(app->cli_ir_rx_queue, &packet, 0, 5); | ||||
|         if(status == osOK) { | ||||
|             printf( | ||||
|                 "%s " | ||||
|                 "Address:0x%02X Command: 0x%02X %s\r\n", | ||||
|                 irda_get_protocol_name(packet.protocol), | ||||
|                 (uint8_t)packet.address, | ||||
|                 (uint8_t)packet.command, | ||||
|                 packet.repeat ? "R" : ""); | ||||
|         } | ||||
|     } | ||||
|     printf("Interrupt command received"); | ||||
|     app->cli_cmd_is_active = false; | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| void irda_cli_cmd_tx(Cli* cli, string_t args, void* context) { | ||||
|     furi_assert(context); | ||||
|     ValueMutex* state_mutex = context; | ||||
|     // Read protocol name
 | ||||
|     IrdaProtocol protocol; | ||||
|     string_t protocol_str; | ||||
|     string_init(protocol_str); | ||||
|     size_t ws = string_search_char(args, ' '); | ||||
|     if(ws == STRING_FAILURE) { | ||||
|         printf("Invalid input. Use ir_tx PROTOCOL ADDRESS COMMAND"); | ||||
|         string_clear(protocol_str); | ||||
|         return; | ||||
|     } else { | ||||
|         string_set_n(protocol_str, args, 0, ws); | ||||
|         string_right(args, ws); | ||||
|         string_strim(args); | ||||
|     } | ||||
|     if(!string_cmp_str(protocol_str, "NEC")) { | ||||
|         protocol = IrdaProtocolNEC; | ||||
|     } else if(!string_cmp_str(protocol_str, "SAMSUNG")) { | ||||
|         protocol = IrdaProtocolSamsung32; | ||||
|     } else { | ||||
|         printf("Incorrect protocol. Valid protocols: `NEC`, `SAMSUNG`"); | ||||
|         string_clear(protocol_str); | ||||
|         return; | ||||
|     } | ||||
|     string_clear(protocol_str); | ||||
|     // Read address
 | ||||
|     uint16_t address = strtoul(string_get_cstr(args), NULL, 16); | ||||
|     ws = string_search_char(args, ' '); | ||||
|     if(!(ws == 4 || ws == 6)) { | ||||
|         printf("Invalid address format. Use 4 [0-F] hex digits in 0xXXXX or XXXX formats"); | ||||
|         return; | ||||
|     } | ||||
|     string_right(args, ws); | ||||
|     string_strim(args); | ||||
|     // Read command
 | ||||
|     uint16_t command = strtoul(string_get_cstr(args), NULL, 16); | ||||
|     ws = string_search_char(args, '\0'); | ||||
|     if(!(ws == 4 || ws == 6)) { | ||||
|         printf("Invalid command format. Use 4 [0-F] hex digits in 0xXXXX or XXXX formats"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     State* state = (State*)acquire_mutex(state_mutex, 25); | ||||
|     if(state == NULL) { | ||||
|         printf("IRDA resources busy\r\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IrdaMessage message = { | ||||
|         .protocol = protocol, | ||||
|         .address = address, | ||||
|         .command = command, | ||||
|     }; | ||||
|     irda_send(&message, 1); | ||||
|     release_mutex(state_mutex, state); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     osMessageQueueId_t event_queue; | ||||
|     IrdaDecoderHandler* handler; | ||||
| } IsrContext; | ||||
| 
 | ||||
| void irda_rx_callback(void* ctx, bool level, uint32_t duration) { | ||||
|     IsrContext* isr_context = ctx; | ||||
|     const IrdaMessage* message = irda_decode(isr_context->handler, level, duration); | ||||
|     AppEvent event; | ||||
|     event.type = EventTypeRX; | ||||
| 
 | ||||
|     if(message) { | ||||
|         event.value.rx = *message; | ||||
|         furi_check(osMessageQueuePut(isr_context->event_queue, &event, 0, 0) == osOK); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int32_t irda2(void* p) { | ||||
|     osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL); | ||||
| 
 | ||||
|     State _state; | ||||
|     uint8_t mode_count = sizeof(modes) / sizeof(modes[0]); | ||||
|     uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]); | ||||
| 
 | ||||
|     _state.carrier_duty_cycle_id = duty_cycles_count - 2; | ||||
|     _state.carrier_freq = 36000; | ||||
|     _state.mode_id = 0; | ||||
|     _state.packet_id = 0; | ||||
| 
 | ||||
|     IrDAApp irda_app; | ||||
|     irda_app.cli = furi_record_open("cli"); | ||||
|     irda_app.cli_ir_rx_queue = osMessageQueueNew(1, sizeof(IrDAPacket), NULL); | ||||
|     irda_app.cli_cmd_is_active = false; | ||||
| 
 | ||||
|     NotificationApp* notification = furi_record_open("notification"); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < IRDA_PACKET_COUNT; i++) { | ||||
|         init_packet(&_state, i, 0, 0, 0); | ||||
|     } | ||||
| 
 | ||||
|     init_packet(&_state, 0, IrdaProtocolNEC, 0x00, 0x11); | ||||
|     init_packet(&_state, 1, IrdaProtocolNEC, 0x08, 0x59); | ||||
|     init_packet(&_state, 2, IrdaProtocolNEC, 0x00, 0x10); | ||||
|     init_packet(&_state, 3, IrdaProtocolNEC, 0x00, 0x15); | ||||
|     init_packet(&_state, 4, IrdaProtocolNEC, 0x00, 0x25); | ||||
|     init_packet(&_state, 5, IrdaProtocolSamsung32, 0x0E, 0x0C); | ||||
|     init_packet(&_state, 6, IrdaProtocolSamsung32, 0x0E, 0x0D); | ||||
|     init_packet(&_state, 7, IrdaProtocolSamsung32, 0x0E, 0x0E); | ||||
| 
 | ||||
|     ValueMutex state_mutex; | ||||
|     if(!init_mutex(&state_mutex, &_state, sizeof(State))) { | ||||
|         printf("cannot create mutex\r\n"); | ||||
|         return 255; | ||||
|     } | ||||
| 
 | ||||
|     ViewPort* view_port = view_port_alloc(); | ||||
| 
 | ||||
|     view_port_draw_callback_set(view_port, render_callback, &state_mutex); | ||||
|     view_port_input_callback_set(view_port, input_callback, event_queue); | ||||
| 
 | ||||
|     cli_add_command(irda_app.cli, "ir_rx", irda_cli_cmd_rx, &irda_app); | ||||
|     cli_add_command(irda_app.cli, "ir_tx", irda_cli_cmd_tx, &state_mutex); | ||||
| 
 | ||||
|     // Open GUI and register view_port
 | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     IsrContext isr_context = { | ||||
|         .handler = irda_alloc_decoder(), | ||||
|         .event_queue = event_queue, | ||||
|     }; | ||||
|     api_hal_irda_rx_irq_init(); | ||||
|     api_hal_irda_rx_irq_set_callback(irda_rx_callback, &isr_context); | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     while(1) { | ||||
|         osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 500); | ||||
| 
 | ||||
|         if(event_status == osOK) { | ||||
|             if(event.type == EventTypeKey) { | ||||
|                 State* state = (State*)acquire_mutex_block(&state_mutex); | ||||
| 
 | ||||
|                 // press events
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyBack) { | ||||
|                     release_mutex(&state_mutex, state); | ||||
| 
 | ||||
|                     // remove all view_ports create by app
 | ||||
|                     gui_remove_view_port(gui, view_port); | ||||
|                     view_port_free(view_port); | ||||
| 
 | ||||
|                     // free decoder
 | ||||
|                     delete_mutex(&state_mutex); | ||||
|                     osMessageQueueDelete(event_queue); | ||||
|                     osMessageQueueDelete(irda_app.cli_ir_rx_queue); | ||||
|                     cli_delete_command(irda_app.cli, "ir_rx"); | ||||
|                     cli_delete_command(irda_app.cli, "ir_tx"); | ||||
|                     furi_record_close("cli"); | ||||
|                     furi_record_close("notification"); | ||||
|                     api_hal_irda_rx_irq_deinit(); | ||||
|                     irda_free_decoder(isr_context.handler); | ||||
| 
 | ||||
|                     // exit
 | ||||
|                     return 0; | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyLeft) { | ||||
|                     if(state->mode_id > 0) { | ||||
|                         state->mode_id--; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyRight) { | ||||
|                     if(state->mode_id < (mode_count - 1)) { | ||||
|                         state->mode_id++; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 modes[state->mode_id].input(&event, state); | ||||
| 
 | ||||
|                 release_mutex(&state_mutex, state); | ||||
|                 view_port_update(view_port); | ||||
| 
 | ||||
|             } else if(event.type == EventTypeRX) { | ||||
|                 notification_message(notification, &sequence_blink_red_10); | ||||
| 
 | ||||
|                 // save only if we in packet mode
 | ||||
|                 State* state = (State*)acquire_mutex_block(&state_mutex); | ||||
|                 IrDAPacket packet = event.value.rx; | ||||
| 
 | ||||
|                 if(state->mode_id == 1) { | ||||
|                     printf("P=%s ", irda_get_protocol_name(packet.protocol)); | ||||
|                     printf("A=0x%02lX ", packet.address); | ||||
|                     printf("C=0x%02lX ", packet.command); | ||||
|                     if(packet.repeat) { | ||||
|                         printf("R"); | ||||
|                     } | ||||
|                     printf("\r\n"); | ||||
|                     // Save packet to state
 | ||||
|                     memcpy(&(state->packets[state->packet_id]), &packet, sizeof(IrDAPacket)); | ||||
|                 } | ||||
|                 if(irda_app.cli_cmd_is_active) { | ||||
|                     // Send decoded packet to cli
 | ||||
|                     osMessageQueuePut(irda_app.cli_ir_rx_queue, &packet, 0, 0); | ||||
|                 } | ||||
| 
 | ||||
|                 release_mutex(&state_mutex, state); | ||||
|                 view_port_update(view_port); | ||||
| 
 | ||||
|                 // blink anyway
 | ||||
|                 notification_message(notification, &sequence_blink_green_10); | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|             // event timeout
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -20,8 +20,10 @@ 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); | ||||
|         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", | ||||
| @ -31,6 +33,13 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|                 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( | ||||
|  | ||||
| @ -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(); | ||||
|     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) { | ||||
|  | ||||
| @ -17,9 +17,10 @@ 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, | ||||
| @ -30,6 +31,12 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | ||||
|             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); | ||||
|     } | ||||
| 
 | ||||
|     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: | ||||
|  | ||||
| @ -1,14 +1,40 @@ | ||||
| #include "../irda-app.hpp" | ||||
| #include "../irda-app-event.hpp" | ||||
| #include <irda_worker.h> | ||||
| 
 | ||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
| 
 | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
| 
 | ||||
|     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 transceiver = app->get_transceiver(); | ||||
|     auto event_queue = view_manager->get_event_queue(); | ||||
| 
 | ||||
|     transceiver->capture_once_start(event_queue); | ||||
| 
 | ||||
|     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(); | ||||
| } | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| #include "gui/canvas.h" | ||||
| #include "irda.h" | ||||
| #include <gui/canvas.h> | ||||
| #include <input/input.h> | ||||
| #include <irda.h> | ||||
| #include <irda_worker.h> | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #include <api-hal-irda.h> | ||||
| #include <api-hal.h> | ||||
| #include <notification/notification-messages.h> | ||||
| #include <gui/view_port.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/elements.h> | ||||
| @ -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,50 +52,13 @@ static void irda_monitor_draw_callback(Canvas* canvas, void* ctx) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int32_t irda_monitor_app(void* p) { | ||||
|     (void)p; | ||||
|     uint32_t counter = 0; | ||||
|     uint32_t print_counter = 0; | ||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
|     IrdaMonitor* irda_monitor = context; | ||||
| 
 | ||||
|     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"); | ||||
|     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); | ||||
| 
 | ||||
|     gui_add_view_port(gui, 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(); | ||||
| 
 | ||||
|     while(1) { | ||||
|         InputEvent event; | ||||
|         if(osOK == osMessageQueueGet(irda_monitor->event_queue, &event, NULL, 50)) { | ||||
|             if((event.type == InputTypeShort) && (event.key == InputKeyBack)) { | ||||
|                 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) { | ||||
|     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), | ||||
| @ -117,14 +69,7 @@ int32_t irda_monitor_app(void* p) { | ||||
|             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) { | ||||
|         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), | ||||
| @ -134,33 +79,59 @@ int32_t irda_monitor_app(void* p) { | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|     } else { | ||||
|                     printf("== unknown data ==\r\n"); | ||||
|         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), | ||||
|                         "unknown data"); | ||||
|                     view_port_update(view_port); | ||||
|             "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("{"); | ||||
|                 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"); | ||||
|     } | ||||
|                 } | ||||
|                 printf("\r\n};\r\n"); | ||||
| } | ||||
| 
 | ||||
| int32_t irda_monitor_app(void* p) { | ||||
|     (void)p; | ||||
| 
 | ||||
|     IrdaMonitor* irda_monitor = furi_alloc(sizeof(IrdaMonitor)); | ||||
|     irda_monitor->display_text[0] = 0; | ||||
|     irda_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL); | ||||
|     irda_monitor->view_port = view_port_alloc(); | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
| 
 | ||||
|     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, irda_monitor->view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     irda_monitor->worker = irda_worker_alloc(); | ||||
|     irda_worker_set_context(irda_monitor->worker, irda_monitor); | ||||
|     irda_worker_start(irda_monitor->worker); | ||||
|     irda_worker_set_received_signal_callback(irda_monitor->worker, signal_received_callback); | ||||
|     irda_worker_enable_blink_on_receiving(irda_monitor->worker, true); | ||||
| 
 | ||||
|     while(1) { | ||||
|         InputEvent event; | ||||
|         if(osOK == osMessageQueueGet(irda_monitor->event_queue, &event, NULL, 50)) { | ||||
|             if((event.type == InputTypeShort) && (event.key == InputKeyBack)) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | ||||
| @ -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(TimerISRCallback callback, void *ctx); | ||||
| 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_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); | ||||
| 
 | ||||
|  | ||||
| @ -1,82 +1,58 @@ | ||||
| #include "cmsis_os.h" | ||||
| #include "api-hal-tim_i.h" | ||||
| #include "api-hal-irda.h" | ||||
| #include <cmsis_os2.h> | ||||
| #include <api-hal-resources.h> | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #include "main.h" | ||||
| #include "api-hal-pwm.h" | ||||
| 
 | ||||
| #include <main.h> | ||||
| #include <api-hal-pwm.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -49,3 +49,7 @@ 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}; | ||||
| 
 | ||||
| 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}; | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -1,17 +1,22 @@ | ||||
| #include "api-hal-interrupt.h" | ||||
| #include "api-hal-irda.h" | ||||
| #include <cmsis_os2.h> | ||||
| #include <api-hal-interrupt.h> | ||||
| #include <api-hal-resources.h> | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #include "main.h" | ||||
| #include "api-hal-pwm.h" | ||||
| #include <main.h> | ||||
| #include <api-hal-pwm.h> | ||||
| 
 | ||||
| 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) { | ||||
|  | ||||
| @ -48,3 +48,7 @@ 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}; | ||||
| 
 | ||||
| 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}; | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| #include "file_reader/file_reader.hpp" | ||||
| #include <file_reader.h> | ||||
| 
 | ||||
| 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); | ||||
|             } | ||||
| 
 | ||||
|             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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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 @@ public: | ||||
|     FS_Api& get_fs_api() { | ||||
|         return *fs_api; | ||||
|     } | ||||
| 
 | ||||
|     void set_max_line_length(size_t value) { | ||||
|         max_line_length = value; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @ -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. | ||||
|  * | ||||
| @ -6,9 +6,11 @@ | ||||
| #include <api-hal-irda.h> | ||||
| #include <api-hal-delay.h> | ||||
| 
 | ||||
| static void irda_set_tx(uint32_t duration, bool level) { | ||||
| #define IRDA_SET_TX_COMMON(d, l)        irda_set_tx((d), (l), IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY) | ||||
| 
 | ||||
| static void irda_set_tx(uint32_t duration, bool level, float duty_cycle, float frequency) { | ||||
|     if (level) { | ||||
|         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); | ||||
							
								
								
									
										41
									
								
								lib/irda/worker/irda_transmit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/irda/worker/irda_transmit.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| #include <api-hal-irda.h> | ||||
| #include <irda.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Send message over IRDA. | ||||
|  * | ||||
|  * \param[in]   message     - message to send. | ||||
|  * \param[in]   times       - number of times message should be sent. | ||||
|  */ | ||||
| void irda_send(const IrdaMessage* message, int times); | ||||
| 
 | ||||
| /**
 | ||||
|  * Send raw data through infrared port. | ||||
|  * | ||||
|  * \param[in]   timings - array of timings to send. | ||||
|  * \param[in]   timings_cnt - timings array size. | ||||
|  * \param[in]   start_from_mark - true if timings starts from mark, | ||||
|  *              otherwise from space | ||||
|  */ | ||||
| void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark); | ||||
| 
 | ||||
| /**
 | ||||
|  * Send raw data through infrared port, with additional settings. | ||||
|  * | ||||
|  * \param[in]   timings - array of timings to send. | ||||
|  * \param[in]   timings_cnt - timings array size. | ||||
|  * \param[in]   start_from_mark - true if timings starts from mark, | ||||
|  *              otherwise from space | ||||
|  * \param[in]   duty_cycle - duty cycle to generate on PWM | ||||
|  * \param[in]   frequency - frequency to generate on PWM | ||||
|  */ | ||||
| void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
							
								
								
									
										237
									
								
								lib/irda/worker/irda_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								lib/irda/worker/irda_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,237 @@ | ||||
| #include "irda_worker.h" | ||||
| #include <irda.h> | ||||
| #include <api-hal-irda.h> | ||||
| #include <limits.h> | ||||
| #include <stdint.h> | ||||
| #include <stream_buffer.h> | ||||
| #include <furi.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| #define MAX_TIMINGS_AMOUNT                  500 | ||||
| #define IRDA_WORKER_RX_TIMEOUT              150 // ms
 | ||||
| #define IRDA_WORKER_RX_RECEIVED             0x01 | ||||
| #define IRDA_WORKER_RX_TIMEOUT_RECEIVED     0x02 | ||||
| #define IRDA_WORKER_OVERRUN                 0x04 | ||||
| #define IRDA_WORKER_EXIT                    0x08 | ||||
| 
 | ||||
| struct IrdaWorkerSignal { | ||||
|     bool decoded; | ||||
|     size_t timings_cnt; | ||||
|     union { | ||||
|         IrdaMessage message; | ||||
|         uint32_t timings[MAX_TIMINGS_AMOUNT]; | ||||
|     } data; | ||||
| }; | ||||
| 
 | ||||
| struct IrdaWorker { | ||||
|     FuriThread* thread; | ||||
|     IrdaDecoderHandler* irda_decoder; | ||||
|     StreamBufferHandle_t stream; | ||||
| 
 | ||||
|     TaskHandle_t worker_handle; | ||||
|     IrdaWorkerSignal signal; | ||||
| 
 | ||||
|     IrdaWorkerReceivedSignalCallback received_signal_callback; | ||||
|     void* context; | ||||
|     bool blink_enable; | ||||
|     bool overrun; | ||||
|     NotificationApp* notification; | ||||
| }; | ||||
| 
 | ||||
| static void irda_worker_rx_timeout_callback(void* context) { | ||||
|     IrdaWorker* instance = context; | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
|     xTaskNotifyFromISR(instance->worker_handle, IRDA_WORKER_RX_TIMEOUT_RECEIVED, eSetBits,  &xHigherPriorityTaskWoken); | ||||
|     portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||
| } | ||||
| 
 | ||||
| static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) { | ||||
|     IrdaWorker* instance = context; | ||||
| 
 | ||||
|     BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||||
|     LevelDuration level_duration = level_duration_make(level, duration); | ||||
| 
 | ||||
|     size_t ret = | ||||
|         xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); | ||||
|     uint32_t notify_value = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN; | ||||
|     xTaskNotifyFromISR(instance->worker_handle, notify_value, eSetBits,  &xHigherPriorityTaskWoken); | ||||
|     portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||
| } | ||||
| 
 | ||||
| static void irda_worker_process_timeout(IrdaWorker* instance) { | ||||
|     if (instance->signal.timings_cnt < 2) | ||||
|         return; | ||||
| 
 | ||||
|     instance->signal.decoded = false; | ||||
|     if (instance->received_signal_callback) | ||||
|         instance->received_signal_callback(instance->context, &instance->signal); | ||||
| } | ||||
| 
 | ||||
| static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, bool level) { | ||||
|     const IrdaMessage* message_decoded = irda_decode(instance->irda_decoder, level, duration); | ||||
|     if (message_decoded) { | ||||
|         instance->signal.data.message = *message_decoded; | ||||
|         instance->signal.timings_cnt = 0; | ||||
|         instance->signal.decoded = true; | ||||
|         if (instance->received_signal_callback) | ||||
|             instance->received_signal_callback(instance->context, &instance->signal); | ||||
|     } else { | ||||
|         /* Skip first timing if it's starts from Space */ | ||||
|         if ((instance->signal.timings_cnt == 0) && !level) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) { | ||||
|             instance->signal.data.timings[instance->signal.timings_cnt] = duration; | ||||
|             ++instance->signal.timings_cnt; | ||||
|         } else { | ||||
|             xTaskNotify(instance->worker_handle, IRDA_WORKER_OVERRUN, eSetBits); | ||||
|             instance->overrun = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int32_t irda_worker_thread_callback(void* context) { | ||||
|     IrdaWorker* instance = context; | ||||
|     uint32_t notify_value = 0; | ||||
|     LevelDuration level_duration; | ||||
|     TickType_t last_blink_time = 0; | ||||
| 
 | ||||
|     while(1) { | ||||
|         BaseType_t result; | ||||
|         result = xTaskNotifyWait(pdFALSE, ULONG_MAX, ¬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; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										91
									
								
								lib/irda/worker/irda_worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								lib/irda/worker/irda_worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <irda.h> | ||||
| #include <api-hal.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /** Interface struct of irda worker */ | ||||
| typedef struct IrdaWorker IrdaWorker; | ||||
| /** Interface struct of received signal */ | ||||
| typedef struct IrdaWorkerSignal IrdaWorkerSignal; | ||||
| 
 | ||||
| /** Callback type to call by IrdaWorker thread when new signal is received */ | ||||
| typedef void (*IrdaWorkerReceivedSignalCallback)(void* context, IrdaWorkerSignal* received_signal); | ||||
| 
 | ||||
| /** Allocate IrdaWorker
 | ||||
|  * | ||||
|  * @return just created instance of IrdaWorker | ||||
|  */ | ||||
| IrdaWorker* irda_worker_alloc(); | ||||
| 
 | ||||
| /** Free IrdaWorker
 | ||||
|  * | ||||
|  * @param[in]   instance - IrdaWorker instance | ||||
|  */ | ||||
| void irda_worker_free(IrdaWorker* instance); | ||||
| 
 | ||||
| /** Received data callback IrdaWorker
 | ||||
|  * | ||||
|  * @param[in]   instance - IrdaWorker instance | ||||
|  * @param[in]   callback - IrdaWorkerReceivedSignalCallback callback | ||||
|  */ | ||||
| void irda_worker_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback); | ||||
| 
 | ||||
| /** Context callback IrdaWorker
 | ||||
|  * | ||||
|  * @param[in]   instance - IrdaWorker instance | ||||
|  * @param[in]   context - context to pass to callbacks | ||||
|  */ | ||||
| void irda_worker_set_context(IrdaWorker* instance, void* context); | ||||
| 
 | ||||
| /** Start IrdaWorker thread, initialise api-hal, prepare all work.
 | ||||
|  * | ||||
|  * @param[in]   instance - IrdaWorker instance | ||||
|  */ | ||||
| void irda_worker_start(IrdaWorker* instance); | ||||
| 
 | ||||
| /** Stop IrdaWorker thread, deinitialize api-hal.
 | ||||
|  * | ||||
|  * @param[in]   instance - IrdaWorker instance | ||||
|  */ | ||||
| void irda_worker_stop(IrdaWorker* instance); | ||||
| 
 | ||||
| /** Clarify is received signal either decoded or raw
 | ||||
|  * | ||||
|  * @param[in]   signal - received signal | ||||
|  * @return      true if signal is decoded, false if signal is raw | ||||
|  */ | ||||
| bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal); | ||||
| 
 | ||||
| /** Acquire raw signal from interface struct 'IrdaWorkerSignal'.
 | ||||
|  * First, you have to ensure that signal is raw. | ||||
|  * | ||||
|  * @param[in]   signal - received signal | ||||
|  * @param[out]  timings - pointer to array of timings | ||||
|  * @param[out]  timings_cnt - pointer to amount of timings | ||||
|  */ | ||||
| void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** timings, size_t* timings_cnt); | ||||
| 
 | ||||
| /** Acquire decoded message from interface struct 'IrdaWorkerSignal'.
 | ||||
|  * First, you have to ensure that signal is decoded. | ||||
|  * | ||||
|  * @param[in]   signal - received signal | ||||
|  * @return      decoded irda message | ||||
|  */ | ||||
| const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal); | ||||
| 
 | ||||
| /** Enable blinking on receiving any signal on IR port.
 | ||||
|  * | ||||
|  * @param[in]   instance - instance of IrdaWorker | ||||
|  * @param[in]   enable - true if you want to enable blinking | ||||
|  *                       false otherwise | ||||
|  */ | ||||
| void irda_worker_enable_blink_on_receiving(IrdaWorker* instance, bool enable); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| @ -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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov