[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 <api-hal-delay.h> | ||||||
| #include "cli/cli.h" | #include <irda.h> | ||||||
| #include "cmsis_os2.h" | #include <app-template.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <cmsis_os2.h> | ||||||
|  | #include <irda_worker.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include "irda.h" |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <string> | #include <string> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
|  | #include <irda_transmit.h> | ||||||
| 
 | 
 | ||||||
| typedef struct IrdaCli { | static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||||
|     IrdaDecoderHandler* handler; |     furi_assert(received_signal); | ||||||
|     osMessageQueueId_t message_queue; |     char buf[100]; | ||||||
| } IrdaCli; |     size_t buf_cnt; | ||||||
|  |     Cli* cli = (Cli*)context; | ||||||
| 
 | 
 | ||||||
| static void irda_rx_callback(void* ctx, bool level, uint32_t duration) { |     if(irda_worker_signal_is_decoded(received_signal)) { | ||||||
|     IrdaCli* irda_cli = (IrdaCli*)ctx; |         const IrdaMessage* message = irda_worker_get_decoded_message(received_signal); | ||||||
|     const IrdaMessage* message; |         buf_cnt = sniprintf( | ||||||
|     message = irda_decode(irda_cli->handler, level, duration); |             buf, | ||||||
|     if(message) { |             sizeof(buf), | ||||||
|         osMessageQueuePut(irda_cli->message_queue, message, 0, 0); |             "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", | ||||||
|  |             irda_get_protocol_name(message->protocol), | ||||||
|  |             irda_get_protocol_address_length(message->protocol), | ||||||
|  |             message->address, | ||||||
|  |             irda_get_protocol_command_length(message->protocol), | ||||||
|  |             message->command, | ||||||
|  |             message->repeat ? " R" : ""); | ||||||
|  |         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||||
|  |     } else { | ||||||
|  |         const uint32_t* timings; | ||||||
|  |         size_t timings_cnt; | ||||||
|  |         irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||||
|  | 
 | ||||||
|  |         buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); | ||||||
|  |         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||||
|  |         for(size_t i = 0; i < timings_cnt; ++i) { | ||||||
|  |             buf_cnt = sniprintf(buf, sizeof(buf), "%lu ", timings[i]); | ||||||
|  |             cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||||
|  |         } | ||||||
|  |         buf_cnt = sniprintf(buf, sizeof(buf), "\r\n"); | ||||||
|  |         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -27,30 +51,19 @@ static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { | |||||||
|         printf("IRDA is busy. Exit."); |         printf("IRDA is busy. Exit."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     IrdaCli irda_cli; | 
 | ||||||
|     irda_cli.handler = irda_alloc_decoder(); |     IrdaWorker* worker = irda_worker_alloc(); | ||||||
|     irda_cli.message_queue = osMessageQueueNew(2, sizeof(IrdaMessage), NULL); |     irda_worker_set_context(worker, cli); | ||||||
|     api_hal_irda_rx_irq_init(); |     irda_worker_start(worker); | ||||||
|     api_hal_irda_rx_irq_set_callback(irda_rx_callback, &irda_cli); |     irda_worker_set_received_signal_callback(worker, signal_received_callback); | ||||||
| 
 | 
 | ||||||
|     printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); |     printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); | ||||||
|     while(!cli_cmd_interrupt_received(cli)) { |     while(!cli_cmd_interrupt_received(cli)) { | ||||||
|         IrdaMessage message; |         delay(50); | ||||||
|         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" : ""); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     api_hal_irda_rx_irq_deinit(); |     irda_worker_stop(worker); | ||||||
|     irda_free_decoder(irda_cli.handler); |     irda_worker_free(worker); | ||||||
|     osMessageQueueDelete(irda_cli.message_queue); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void irda_cli_print_usage(void) { | static void irda_cli_print_usage(void) { | ||||||
| @ -63,38 +76,93 @@ static void irda_cli_print_usage(void) { | |||||||
|     printf("\r\n"); |     printf("\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static 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) { | static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | ||||||
|     if(api_hal_irda_rx_irq_is_busy()) { |     if(api_hal_irda_rx_irq_is_busy()) { | ||||||
|         printf("IRDA is busy. Exit."); |         printf("IRDA is busy. Exit."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     uint32_t command = 0; |     IrdaMessage message; | ||||||
|     uint32_t address = 0; |     const char* str = string_get_cstr(args); | ||||||
|     char protocol_name[32]; |     float frequency; | ||||||
|     int parsed = sscanf(string_get_cstr(args), "%31s %lX %lX", protocol_name, &address, &command); |     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"); |         printf("Wrong arguments.\r\n"); | ||||||
|         irda_cli_print_usage(); |         irda_cli_print_usage(); | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name); |     free(timings); | ||||||
| 
 |  | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" void irda_cli_init() { | extern "C" void irda_cli_init() { | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ bool IrdaAppBruteForce::calculate_messages() { | |||||||
| 
 | 
 | ||||||
|     file_parser.reset(); |     file_parser.reset(); | ||||||
|     while(1) { |     while(1) { | ||||||
|         auto message = file_parser.read_message(&file); |         auto message = file_parser.read_signal(&file); | ||||||
|         if(!message) break; |         if(!message) break; | ||||||
|         auto element = records.find(message->name); |         auto element = records.find(message->name); | ||||||
|         if(element != records.cend()) { |         if(element != records.cend()) { | ||||||
| @ -37,19 +37,19 @@ void IrdaAppBruteForce::stop_bruteforce() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: [FL-1418] replace with timer-chained consequence of messages.
 | // TODO: [FL-1418] replace with timer-chained consequence of messages.
 | ||||||
| bool IrdaAppBruteForce::send_next_bruteforce(const IrdaAppSignalTransceiver& transceiver) { | bool IrdaAppBruteForce::send_next_bruteforce(void) { | ||||||
|     furi_assert(current_record.size()); |     furi_assert(current_record.size()); | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> message; |     std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> file_signal; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         message = file_parser.read_message(&file); |         file_signal = file_parser.read_signal(&file); | ||||||
|     } while(message && current_record.compare(message->name)); |     } while(file_signal && current_record.compare(file_signal->name)); | ||||||
| 
 | 
 | ||||||
|     if(message) { |     if(file_signal) { | ||||||
|         transceiver.send_message(&message->message); |         file_signal->signal.transmit(); | ||||||
|     } |     } | ||||||
|     return !!message; |     return !!file_signal; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) { | bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) { | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| #include "furi/check.h" | #include "furi/check.h" | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include "irda-app-file-parser.hpp" | #include "irda-app-file-parser.hpp" | ||||||
| #include "irda-app-transceiver.hpp" |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IrdaAppBruteForce { | class IrdaAppBruteForce { | ||||||
| @ -26,7 +25,7 @@ class IrdaAppBruteForce { | |||||||
| public: | public: | ||||||
|     bool calculate_messages(); |     bool calculate_messages(); | ||||||
|     void stop_bruteforce(); |     void stop_bruteforce(); | ||||||
|     bool send_next_bruteforce(const IrdaAppSignalTransceiver& receiver); |     bool send_next_bruteforce(); | ||||||
|     bool start_bruteforce(int index, int& record_amount); |     bool start_bruteforce(int index, int& record_amount); | ||||||
|     void add_record(int index, const char* name); |     void add_record(int index, const char* name); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,26 +1,83 @@ | |||||||
| #include "irda-app-file-parser.hpp" | #include "irda-app-file-parser.hpp" | ||||||
|  | #include "irda-app-remote-manager.hpp" | ||||||
|  | #include "irda-app-signal.h" | ||||||
|  | #include <irda.h> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string_view> | ||||||
|  | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> IrdaAppFileParser::read_message(File* file) { | uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100); | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> IrdaAppFileParser::read_signal(File* file) { | ||||||
|     while(1) { |     while(1) { | ||||||
|         auto str = getline(file); |         auto str = getline(file); | ||||||
|         if(str.empty()) return nullptr; |         if(str.empty()) return nullptr; | ||||||
| 
 | 
 | ||||||
|         auto message = parse_message(str); |         auto message = parse_signal(str); | ||||||
|  |         if(!message.get()) { | ||||||
|  |             message = parse_signal_raw(str); | ||||||
|  |         } | ||||||
|         if(message) return message; |         if(message) return message; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> | bool IrdaAppFileParser::store_signal(File* file, const IrdaAppSignal& signal, const char* name) { | ||||||
|     IrdaAppFileParser::parse_message(const std::string& str) const { |     char* content = new char[max_line_length]; | ||||||
|  |     size_t written = 0; | ||||||
|  | 
 | ||||||
|  |     if(!signal.is_raw()) { | ||||||
|  |         auto message = signal.get_message(); | ||||||
|  |         auto protocol = message.protocol; | ||||||
|  | 
 | ||||||
|  |         sniprintf( | ||||||
|  |             content, | ||||||
|  |             max_line_length, | ||||||
|  |             "%.31s %.31s A:%0*lX C:%0*lX\n", | ||||||
|  |             name, | ||||||
|  |             irda_get_protocol_name(protocol), | ||||||
|  |             irda_get_protocol_address_length(protocol), | ||||||
|  |             message.address, | ||||||
|  |             irda_get_protocol_command_length(protocol), | ||||||
|  |             message.command); | ||||||
|  |         written = strlen(content); | ||||||
|  |     } else { | ||||||
|  |         int duty_cycle = 100 * IRDA_COMMON_DUTY_CYCLE; | ||||||
|  |         written += sniprintf( | ||||||
|  |             &content[written], | ||||||
|  |             max_line_length - written, | ||||||
|  |             "%.31s RAW F:%d DC:%d", | ||||||
|  |             name, | ||||||
|  |             IRDA_COMMON_CARRIER_FREQUENCY, | ||||||
|  |             duty_cycle); | ||||||
|  | 
 | ||||||
|  |         auto& raw_signal = signal.get_raw_signal(); | ||||||
|  |         for(size_t i = 0; i < raw_signal.timings_cnt; ++i) { | ||||||
|  |             written += sniprintf( | ||||||
|  |                 &content[written], max_line_length - written, " %ld", raw_signal.timings[i]); | ||||||
|  |             furi_assert(written <= max_line_length); | ||||||
|  |         } | ||||||
|  |         written += snprintf(&content[written], max_line_length - written, "\n"); | ||||||
|  |     } | ||||||
|  |     furi_assert(written < max_line_length); | ||||||
|  | 
 | ||||||
|  |     size_t write_count = 0; | ||||||
|  |     write_count = get_fs_api().file.write(file, content, written); | ||||||
|  |     delete[] content; | ||||||
|  |     return (file->error_id == FSE_OK) && (write_count == written); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> | ||||||
|  |     IrdaAppFileParser::parse_signal(const std::string& str) const { | ||||||
|     char protocol_name[32]; |     char protocol_name[32]; | ||||||
|     uint32_t address; |     uint32_t address; | ||||||
|     uint32_t command; |     uint32_t command; | ||||||
|     auto irda_file_message = std::make_unique<IrdaFileMessage>(); |     auto irda_file_signal = std::make_unique<IrdaFileSignal>(); | ||||||
| 
 | 
 | ||||||
|     int parsed = std::sscanf( |     int parsed = std::sscanf( | ||||||
|         str.c_str(), |         str.c_str(), | ||||||
|         "%31s %31s A:%lX C:%lX", |         "%31s %31s A:%lX C:%lX", | ||||||
|         irda_file_message->name, |         irda_file_signal->name, | ||||||
|         protocol_name, |         protocol_name, | ||||||
|         &address, |         &address, | ||||||
|         &command); |         &command); | ||||||
| @ -47,12 +104,97 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> | |||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     irda_file_message->message = { |     IrdaMessage message = { | ||||||
|         .protocol = protocol, |         .protocol = protocol, | ||||||
|         .address = address, |         .address = address, | ||||||
|         .command = command, |         .command = command, | ||||||
|         .repeat = false, |         .repeat = false, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return irda_file_message; |     irda_file_signal->signal.set_message(&message); | ||||||
|  | 
 | ||||||
|  |     return irda_file_signal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* find_first_not_of(const char* str, char symbol) { | ||||||
|  |     const char* str_start = nullptr; | ||||||
|  |     while(str != str_start) { | ||||||
|  |         str_start = str; | ||||||
|  |         str = strchr(str, symbol); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return str; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> | ||||||
|  |     IrdaAppFileParser::parse_signal_raw(const std::string& string) const { | ||||||
|  |     char protocol_name[32]; | ||||||
|  |     uint32_t frequency; | ||||||
|  |     uint32_t duty_cycle; | ||||||
|  |     int str_len = string.size(); | ||||||
|  |     std::string_view str(string.c_str()); | ||||||
|  |     auto irda_file_signal = std::make_unique<IrdaFileSignal>(); | ||||||
|  | 
 | ||||||
|  |     int parsed = std::sscanf( | ||||||
|  |         str.data(), | ||||||
|  |         "%31s %31s F:%ld DC:%ld", | ||||||
|  |         irda_file_signal->name, | ||||||
|  |         protocol_name, | ||||||
|  |         &frequency, | ||||||
|  |         &duty_cycle); | ||||||
|  | 
 | ||||||
|  |     if(parsed != 4) { | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     char dummy[100] = {0}; | ||||||
|  |     int header_len = 0; | ||||||
|  |     header_len = sniprintf( | ||||||
|  |         dummy, | ||||||
|  |         sizeof(dummy), | ||||||
|  |         "%.31s %.31s F:%ld DC:%ld", | ||||||
|  |         irda_file_signal->name, | ||||||
|  |         protocol_name, | ||||||
|  |         frequency, | ||||||
|  |         duty_cycle); | ||||||
|  | 
 | ||||||
|  |     furi_assert(header_len < str_len); | ||||||
|  |     str.remove_prefix(header_len); | ||||||
|  | 
 | ||||||
|  |     /* move allocated timings into raw signal object */ | ||||||
|  |     IrdaAppSignal::RawSignal raw_signal = {.timings_cnt = 0, .timings = new uint32_t[500]}; | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     while(!str.empty()) { | ||||||
|  |         char buf[10]; | ||||||
|  |         size_t index = str.find_first_not_of(' ', 1); | ||||||
|  |         if(index == std::string_view::npos) { | ||||||
|  |             result = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         str.remove_prefix(index); | ||||||
|  |         parsed = std::sscanf(str.data(), "%9s", buf); | ||||||
|  |         if(parsed != 1) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         str.remove_prefix(strlen(buf)); | ||||||
|  | 
 | ||||||
|  |         int value = atoi(buf); | ||||||
|  |         if(value <= 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         raw_signal.timings[raw_signal.timings_cnt] = value; | ||||||
|  |         ++raw_signal.timings_cnt; | ||||||
|  |         if(raw_signal.timings_cnt >= 500) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(result) { | ||||||
|  |         irda_file_signal->signal.set_raw_signal(raw_signal.timings, raw_signal.timings_cnt); | ||||||
|  |     } else { | ||||||
|  |         (void)irda_file_signal.release(); | ||||||
|  |         delete[] raw_signal.timings; | ||||||
|  |     } | ||||||
|  |     return irda_file_signal; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,17 +1,26 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "file_reader/file_reader.hpp" | #include <file_reader/file_reader.h> | ||||||
| #include "irda.h" | #include <irda.h> | ||||||
|  | #include "irda-app-remote-manager.hpp" | ||||||
| 
 | 
 | ||||||
| class IrdaAppFileParser : public FileReader { | class IrdaAppFileParser : public FileReader { | ||||||
| public: | public: | ||||||
|     typedef struct { |     typedef struct { | ||||||
|         char name[32]; |         char name[32]; | ||||||
|         IrdaMessage message; |         IrdaAppSignal signal; | ||||||
|     } IrdaFileMessage; |     } IrdaFileSignal; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> read_message(File* file); |     IrdaAppFileParser() { | ||||||
|  |         /* Assume we can save max 512 samples */ | ||||||
|  |         set_max_line_length(max_line_length); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> read_signal(File* file); | ||||||
|  |     bool store_signal(File* file, const IrdaAppSignal& signal, const char* name); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::unique_ptr<IrdaFileMessage> parse_message(const std::string& str) const; |     static const uint32_t max_line_length; | ||||||
|  |     std::unique_ptr<IrdaFileSignal> parse_signal(const std::string& str) const; | ||||||
|  |     std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,16 +33,15 @@ static std::string | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaAppSignal& signal) { | ||||||
|     remote->buttons.emplace_back(button_name, message); |     remote->buttons.emplace_back(button_name, signal); | ||||||
|     return store(); |     return store(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::add_remote_with_button( | bool IrdaAppRemoteManager::add_remote_with_button( | ||||||
|     const char* button_name, |     const char* button_name, | ||||||
|     const IrdaMessage* message) { |     const IrdaAppSignal& signal) { | ||||||
|     furi_check(button_name != nullptr); |     furi_check(button_name != nullptr); | ||||||
|     furi_check(message != nullptr); |  | ||||||
| 
 | 
 | ||||||
|     std::vector<std::string> remote_list; |     std::vector<std::string> remote_list; | ||||||
|     bool result = get_remote_list(remote_list); |     bool result = get_remote_list(remote_list); | ||||||
| @ -51,7 +50,7 @@ bool IrdaAppRemoteManager::add_remote_with_button( | |||||||
|     auto new_name = find_vacant_name(remote_list, default_remote_name); |     auto new_name = find_vacant_name(remote_list, default_remote_name); | ||||||
| 
 | 
 | ||||||
|     remote = std::make_unique<IrdaAppRemote>(new_name); |     remote = std::make_unique<IrdaAppRemote>(new_name); | ||||||
|     return add_button(button_name, message); |     return add_button(button_name, signal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppRemote::IrdaAppRemote(const std::string& name) | IrdaAppRemote::IrdaAppRemote(const std::string& name) | ||||||
| @ -70,12 +69,12 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | |||||||
|     return name_vector; |     return name_vector; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t index) const { | const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const { | ||||||
|     furi_check(remote.get() != nullptr); |     furi_check(remote.get() != nullptr); | ||||||
|     auto& buttons = remote->buttons; |     auto& buttons = remote->buttons; | ||||||
|     furi_check(index < buttons.size()); |     furi_check(index < buttons.size()); | ||||||
| 
 | 
 | ||||||
|     return &buttons.at(index).message; |     return buttons.at(index).signal; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { | std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { | ||||||
| @ -166,7 +165,6 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() { | |||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::store(void) { | bool IrdaAppRemoteManager::store(void) { | ||||||
|     File file; |     File file; | ||||||
|     uint16_t write_count; |  | ||||||
|     std::string dirname(std::string("/") + irda_directory); |     std::string dirname(std::string("/") + irda_directory); | ||||||
| 
 | 
 | ||||||
|     IrdaAppFileParser file_parser; |     IrdaAppFileParser file_parser; | ||||||
| @ -186,25 +184,9 @@ bool IrdaAppRemoteManager::store(void) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     char content[128]; |  | ||||||
| 
 |  | ||||||
|     for(const auto& button : remote->buttons) { |     for(const auto& button : remote->buttons) { | ||||||
|         auto protocol = button.message.protocol; |         bool result = file_parser.store_signal(&file, button.signal, button.name.c_str()); | ||||||
| 
 |         if(!result) { | ||||||
|         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) { |  | ||||||
|             file_parser.get_sd_api().show_error( |             file_parser.get_sd_api().show_error( | ||||||
|                 file_parser.get_sd_api().context, "Cannot write\nto key file"); |                 file_parser.get_sd_api().context, "Cannot write\nto key file"); | ||||||
|             file_parser.get_fs_api().file.close(&file); |             file_parser.get_fs_api().file.close(&file); | ||||||
| @ -267,9 +249,9 @@ bool IrdaAppRemoteManager::load(const std::string& name) { | |||||||
|     remote = std::make_unique<IrdaAppRemote>(name); |     remote = std::make_unique<IrdaAppRemote>(name); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         auto message = file_parser.read_message(&file); |         auto file_signal = file_parser.read_signal(&file); | ||||||
|         if(!message) break; |         if(!file_signal.get()) break; | ||||||
|         remote->buttons.emplace_back(message->name, &message->message); |         remote->buttons.emplace_back(file_signal->name, file_signal->signal); | ||||||
|     } |     } | ||||||
|     file_parser.get_fs_api().file.close(&file); |     file_parser.get_fs_api().file.close(&file); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include <irda_worker.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| @ -6,14 +7,16 @@ | |||||||
| #include <irda.h> | #include <irda.h> | ||||||
| #include <sd-card-api.h> | #include <sd-card-api.h> | ||||||
| #include <filesystem-api.h> | #include <filesystem-api.h> | ||||||
|  | #include "irda-app-signal.h" | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class IrdaAppRemoteButton { | class IrdaAppRemoteButton { | ||||||
|     friend class IrdaAppRemoteManager; |     friend class IrdaAppRemoteManager; | ||||||
|     std::string name; |     std::string name; | ||||||
|     IrdaMessage message; |     IrdaAppSignal signal; | ||||||
| public: | public: | ||||||
|     IrdaAppRemoteButton(const char* name, const IrdaMessage* message) |     IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal) | ||||||
|         : name(name), message (*message) {} |         : name(name), signal (signal) {} | ||||||
|     ~IrdaAppRemoteButton() {} |     ~IrdaAppRemoteButton() {} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -38,8 +41,8 @@ class IrdaAppRemoteManager { | |||||||
|     std::string make_filename(const std::string& name) const; |     std::string make_filename(const std::string& name) const; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     bool add_remote_with_button(const char* button_name, const IrdaMessage* message); |     bool add_remote_with_button(const char* button_name, const IrdaAppSignal& signal); | ||||||
|     bool add_button(const char* button_name, const IrdaMessage* message); |     bool add_button(const char* button_name, const IrdaAppSignal& signal); | ||||||
| 
 | 
 | ||||||
|     int find_remote_name(const std::vector<std::string>& strings); |     int find_remote_name(const std::vector<std::string>& strings); | ||||||
|     bool rename_button(uint32_t index, const char* str); |     bool rename_button(uint32_t index, const char* str); | ||||||
| @ -50,7 +53,7 @@ public: | |||||||
|     std::string get_button_name(uint32_t index); |     std::string get_button_name(uint32_t index); | ||||||
|     std::string get_remote_name(); |     std::string get_remote_name(); | ||||||
|     size_t get_number_of_buttons(); |     size_t get_number_of_buttons(); | ||||||
|     const IrdaMessage* get_button_data(size_t button_index) const; |     const IrdaAppSignal& get_button_data(size_t index) const; | ||||||
|     bool delete_button(uint32_t index); |     bool delete_button(uint32_t index); | ||||||
|     bool delete_remote(); |     bool delete_remote(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										95
									
								
								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-app.hpp" | ||||||
|  | #include <irda_worker.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
| @ -99,10 +100,6 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() { | |||||||
|     return &remote_manager; |     return &remote_manager; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppSignalTransceiver* IrdaApp::get_transceiver() { |  | ||||||
|     return &transceiver; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void IrdaApp::set_text_store(uint8_t index, const char* text...) { | void IrdaApp::set_text_store(uint8_t index, const char* text...) { | ||||||
|     furi_check(index < text_store_max); |     furi_check(index < text_store_max); | ||||||
| 
 | 
 | ||||||
| @ -220,3 +217,15 @@ void IrdaApp::notify_green_on() { | |||||||
| void IrdaApp::notify_green_off() { | void IrdaApp::notify_green_off() { | ||||||
|     notification_message(notification, &sequence_reset_green); |     notification_message(notification, &sequence_reset_green); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | IrdaWorker* IrdaApp::get_irda_worker() { | ||||||
|  |     return irda_worker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const IrdaAppSignal& IrdaApp::get_received_signal() const { | ||||||
|  |     return received_signal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::set_received_signal(const IrdaAppSignal& signal) { | ||||||
|  |     received_signal = signal; | ||||||
|  | } | ||||||
|  | |||||||
| @ -7,10 +7,10 @@ | |||||||
| #include "scene/irda-app-scene.hpp" | #include "scene/irda-app-scene.hpp" | ||||||
| #include "irda-app-view-manager.hpp" | #include "irda-app-view-manager.hpp" | ||||||
| #include "irda-app-remote-manager.hpp" | #include "irda-app-remote-manager.hpp" | ||||||
| #include "irda-app-transceiver.hpp" |  | ||||||
| #include <forward_list> | #include <forward_list> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
|  | #include <irda_worker.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IrdaApp { | class IrdaApp { | ||||||
| @ -51,11 +51,15 @@ public: | |||||||
|     bool switch_to_previous_scene(uint8_t count = 1); |     bool switch_to_previous_scene(uint8_t count = 1); | ||||||
|     Scene get_previous_scene(); |     Scene get_previous_scene(); | ||||||
|     IrdaAppViewManager* get_view_manager(); |     IrdaAppViewManager* get_view_manager(); | ||||||
|     IrdaAppSignalTransceiver* get_transceiver(); |  | ||||||
|     void set_text_store(uint8_t index, const char* text...); |     void set_text_store(uint8_t index, const char* text...); | ||||||
|     char* get_text_store(uint8_t index); |     char* get_text_store(uint8_t index); | ||||||
|     uint8_t get_text_store_size(); |     uint8_t get_text_store_size(); | ||||||
|     IrdaAppRemoteManager* get_remote_manager(); |     IrdaAppRemoteManager* get_remote_manager(); | ||||||
|  | 
 | ||||||
|  |     IrdaWorker* get_irda_worker(); | ||||||
|  |     const IrdaAppSignal& get_received_signal() const; | ||||||
|  |     void set_received_signal(const IrdaAppSignal& signal); | ||||||
|  | 
 | ||||||
|     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list); |     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list); | ||||||
| 
 | 
 | ||||||
|     void set_edit_element(EditElement value); |     void set_edit_element(EditElement value); | ||||||
| @ -87,8 +91,10 @@ public: | |||||||
| 
 | 
 | ||||||
|     IrdaApp() { |     IrdaApp() { | ||||||
|         notification = static_cast<NotificationApp*>(furi_record_open("notification")); |         notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||||
|  |         irda_worker = irda_worker_alloc(); | ||||||
|     } |     } | ||||||
|     ~IrdaApp() { |     ~IrdaApp() { | ||||||
|  |         irda_worker_free(irda_worker); | ||||||
|         furi_record_close("notification"); |         furi_record_close("notification"); | ||||||
|         for (auto &it : scenes) |         for (auto &it : scenes) | ||||||
|             delete it.second; |             delete it.second; | ||||||
| @ -103,9 +109,10 @@ private: | |||||||
|     uint32_t current_button; |     uint32_t current_button; | ||||||
| 
 | 
 | ||||||
|     NotificationApp* notification; |     NotificationApp* notification; | ||||||
|     IrdaAppSignalTransceiver transceiver; |  | ||||||
|     IrdaAppViewManager view_manager; |     IrdaAppViewManager view_manager; | ||||||
|     IrdaAppRemoteManager remote_manager; |     IrdaAppRemoteManager remote_manager; | ||||||
|  |     IrdaWorker* irda_worker; | ||||||
|  |     IrdaAppSignal received_signal; | ||||||
| 
 | 
 | ||||||
|     std::forward_list<Scene> previous_scenes_list; |     std::forward_list<Scene> previous_scenes_list; | ||||||
|     Scene current_scene = Scene::Start; |     Scene current_scene = Scene::Start; | ||||||
|  | |||||||
| @ -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(); |     auto remote_manager = app->get_remote_manager(); | ||||||
| 
 | 
 | ||||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { |     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||||
|         auto message = remote_manager->get_button_data(app->get_current_button()); |         auto signal = remote_manager->get_button_data(app->get_current_button()); | ||||||
|         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); |         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); | ||||||
|  |         if(!signal.is_raw()) { | ||||||
|  |             auto message = &signal.get_message(); | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|                 0, |                 0, | ||||||
|                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", |                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", | ||||||
| @ -31,6 +33,13 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|                 message->address, |                 message->address, | ||||||
|                 irda_get_protocol_command_length(message->protocol), |                 irda_get_protocol_command_length(message->protocol), | ||||||
|                 message->command); |                 message->command); | ||||||
|  |         } else { | ||||||
|  |             app->set_text_store( | ||||||
|  |                 0, | ||||||
|  |                 "%s\nRAW\n%ld samples", | ||||||
|  |                 remote_manager->get_button_name(app->get_current_button()).c_str(), | ||||||
|  |                 signal.get_raw_signal().timings_cnt); | ||||||
|  |         } | ||||||
|     } else { |     } else { | ||||||
|         dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter); |         dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter); | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|  | |||||||
| @ -5,15 +5,20 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | |||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     TextInput* text_input = view_manager->get_text_input(); |     TextInput* text_input = view_manager->get_text_input(); | ||||||
| 
 | 
 | ||||||
|     auto transceiver = app->get_transceiver(); |     auto signal = app->get_received_signal(); | ||||||
|     auto message = transceiver->get_last_message(); |  | ||||||
| 
 | 
 | ||||||
|  |     if(!signal.is_raw()) { | ||||||
|  |         auto message = &signal.get_message(); | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             0, |             0, | ||||||
|             "%.4s_%0*lX", |             "%.4s_%0*lX", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_command_length(message->protocol), |             irda_get_protocol_command_length(message->protocol), | ||||||
|             message->command); |             message->command); | ||||||
|  |     } else { | ||||||
|  |         auto raw_signal = signal.get_raw_signal(); | ||||||
|  |         app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     text_input_set_header_text(text_input, "Name the key"); |     text_input_set_header_text(text_input, "Name the key"); | ||||||
|     text_input_set_result_callback( |     text_input_set_result_callback( | ||||||
| @ -31,14 +36,13 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { |     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||||
|         auto remote_manager = app->get_remote_manager(); |         auto remote_manager = app->get_remote_manager(); | ||||||
|         auto transceiver = app->get_transceiver(); |  | ||||||
|         bool result = false; |         bool result = false; | ||||||
|         if(app->get_learn_new_remote()) { |         if(app->get_learn_new_remote()) { | ||||||
|             result = remote_manager->add_remote_with_button( |             result = remote_manager->add_remote_with_button( | ||||||
|                 app->get_text_store(0), transceiver->get_last_message()); |                 app->get_text_store(0), app->get_received_signal()); | ||||||
|         } else { |         } else { | ||||||
|             result = remote_manager->add_button( |             result = | ||||||
|                 app->get_text_store(0), transceiver->get_last_message()); |                 remote_manager->add_button(app->get_text_store(0), app->get_received_signal()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!result) { |         if(!result) { | ||||||
|  | |||||||
| @ -17,9 +17,10 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
| 
 | 
 | ||||||
|     app->notify_green_on(); |     app->notify_green_on(); | ||||||
| 
 | 
 | ||||||
|     auto transceiver = app->get_transceiver(); |     auto signal = app->get_received_signal(); | ||||||
|     auto message = transceiver->get_last_message(); |  | ||||||
| 
 | 
 | ||||||
|  |     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(0, "%s", irda_get_protocol_name(message->protocol)); | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             1, |             1, | ||||||
| @ -30,6 +31,12 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
|             message->command); |             message->command); | ||||||
|         dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter); |         dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter); | ||||||
|         dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); |         dialog_ex_set_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_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Save"); |     dialog_ex_set_right_button_text(dialog_ex, "Save"); | ||||||
|     dialog_ex_set_center_button_text(dialog_ex, "Send"); |     dialog_ex_set_center_button_text(dialog_ex, "Send"); | ||||||
| @ -50,9 +57,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|             break; |             break; | ||||||
|         case DialogExResultCenter: { |         case DialogExResultCenter: { | ||||||
|             app->notify_space_blink(); |             app->notify_space_blink(); | ||||||
|             auto transceiver = app->get_transceiver(); |             auto signal = app->get_received_signal(); | ||||||
|             auto message = transceiver->get_last_message(); |             signal.transmit(); | ||||||
|             irda_send(message, 1); |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case DialogExResultRight: |         case DialogExResultRight: | ||||||
|  | |||||||
| @ -1,14 +1,40 @@ | |||||||
| #include "../irda-app.hpp" | #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) { | void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | ||||||
|     auto view_manager = app->get_view_manager(); |     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 popup = view_manager->get_popup(); | ||||||
| 
 | 
 | ||||||
|  |     auto worker = app->get_irda_worker(); | ||||||
|  |     irda_worker_set_context(worker, app); | ||||||
|  |     irda_worker_set_received_signal_callback(worker, signal_received_callback); | ||||||
|  |     irda_worker_start(worker); | ||||||
|  | 
 | ||||||
|     popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31); |     popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31); | ||||||
|     popup_set_text( |     popup_set_text( | ||||||
|         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); |         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); | ||||||
| @ -24,19 +50,27 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | |||||||
| bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::Tick) { |     switch(event->type) { | ||||||
|  |     case IrdaAppEvent::Type::Tick: | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         app->notify_red_blink(); |         app->notify_red_blink(); | ||||||
|     } |         break; | ||||||
|     if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { |     case IrdaAppEvent::Type::IrdaMessageReceived: | ||||||
|         app->notify_success(); |         app->notify_success(); | ||||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); |         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); | ||||||
|  |         irda_worker_stop(app->get_irda_worker()); | ||||||
|  |         break; | ||||||
|  |     case IrdaAppEvent::Type::Back: | ||||||
|  |         consumed = true; | ||||||
|  |         irda_worker_stop(app->get_irda_worker()); | ||||||
|  |         app->switch_to_previous_scene(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_assert(0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearn::on_exit(IrdaApp* app) { | void IrdaAppSceneLearn::on_exit(IrdaApp* app) { | ||||||
|     auto transceiver = app->get_transceiver(); |  | ||||||
|     transceiver->capture_stop(); |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -64,8 +64,8 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|         default: |         default: | ||||||
|             app->notify_click_and_blink(); |             app->notify_click_and_blink(); | ||||||
|             auto remote_manager = app->get_remote_manager(); |             auto remote_manager = app->get_remote_manager(); | ||||||
|             auto message = remote_manager->get_button_data(event->payload.menu_index); |             auto signal = remote_manager->get_button_data(event->payload.menu_index); | ||||||
|             app->get_transceiver()->send_message(message); |             signal.transmit(); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } else if(event->type == IrdaAppEvent::Type::Back) { |     } 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(); |             auto view_manager = app->get_view_manager(); | ||||||
|             IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; |             IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; | ||||||
|             view_manager->send_event(&tick_event); |             view_manager->send_event(&tick_event); | ||||||
|             if(brute_force.send_next_bruteforce(*app->get_transceiver())) { |             if(brute_force.send_next_bruteforce()) { | ||||||
|                 progress_popup(app); |                 progress_popup(app); | ||||||
|             } else { |             } else { | ||||||
|                 brute_force.stop_bruteforce(); |                 brute_force.stop_bruteforce(); | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| #include "gui/canvas.h" | #include <gui/canvas.h> | ||||||
| #include "irda.h" | #include <input/input.h> | ||||||
|  | #include <irda.h> | ||||||
|  | #include <irda_worker.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include <api-hal.h> | #include <api-hal.h> | ||||||
| #include <notification/notification-messages.h> |  | ||||||
| #include <gui/view_port.h> | #include <gui/view_port.h> | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| @ -20,25 +21,13 @@ typedef struct { | |||||||
| } IrdaDelaysArray; | } IrdaDelaysArray; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     IrdaDecoderHandler* handler; |  | ||||||
|     char display_text[64]; |     char display_text[64]; | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
|     IrdaDelaysArray delays; |     IrdaDelaysArray delays; | ||||||
|  |     IrdaWorker* worker; | ||||||
|  |     ViewPort* view_port; | ||||||
| } IrdaMonitor; | } IrdaMonitor; | ||||||
| 
 | 
 | ||||||
| static void irda_rx_callback(void* ctx, bool level, uint32_t duration) { |  | ||||||
|     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx; |  | ||||||
|     IrdaDelaysArray* delays = &irda_monitor->delays; |  | ||||||
| 
 |  | ||||||
|     if(delays->timing_cnt > 1) furi_assert(level != delays->timing[delays->timing_cnt - 1].level); |  | ||||||
|     delays->timing[delays->timing_cnt].level = level; |  | ||||||
|     delays->timing[delays->timing_cnt].duration = duration; |  | ||||||
|     delays->timing_cnt++; // Read-Modify-Write in ISR only: no need to add synchronization
 |  | ||||||
|     if(delays->timing_cnt >= IRDA_TIMINGS_SIZE) { |  | ||||||
|         delays->timing_cnt = 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void irda_monitor_input_callback(InputEvent* input_event, void* ctx) { | void irda_monitor_input_callback(InputEvent* input_event, void* ctx) { | ||||||
|     furi_assert(ctx); |     furi_assert(ctx); | ||||||
|     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx; |     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx; | ||||||
| @ -63,50 +52,13 @@ static void irda_monitor_draw_callback(Canvas* canvas, void* ctx) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t irda_monitor_app(void* p) { | static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||||
|     (void)p; |     furi_assert(context); | ||||||
|     uint32_t counter = 0; |     furi_assert(received_signal); | ||||||
|     uint32_t print_counter = 0; |     IrdaMonitor* irda_monitor = context; | ||||||
| 
 | 
 | ||||||
|     IrdaMonitor* irda_monitor = furi_alloc(sizeof(IrdaMonitor)); |     if(irda_worker_signal_is_decoded(received_signal)) { | ||||||
|     irda_monitor->display_text[0] = 0; |         const IrdaMessage* message = irda_worker_get_decoded_message(received_signal); | ||||||
|     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) { |  | ||||||
|         snprintf( |         snprintf( | ||||||
|             irda_monitor->display_text, |             irda_monitor->display_text, | ||||||
|             sizeof(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), |             irda_get_protocol_command_length(message->protocol), | ||||||
|             message->command, |             message->command, | ||||||
|             message->repeat ? " R" : ""); |             message->repeat ? " R" : ""); | ||||||
|                 view_port_update(view_port); |         view_port_update(irda_monitor->view_port); | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             size_t distance = (counter > print_counter) ? |  | ||||||
|                                   counter - print_counter : |  | ||||||
|                                   (counter + IRDA_TIMINGS_SIZE) - print_counter; |  | ||||||
|             if(message || (distance > (IRDA_TIMINGS_SIZE / 2))) { |  | ||||||
|                 if(message) { |  | ||||||
|         printf( |         printf( | ||||||
|             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", |             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
| @ -134,33 +79,59 @@ int32_t irda_monitor_app(void* p) { | |||||||
|             message->command, |             message->command, | ||||||
|             message->repeat ? " R" : ""); |             message->repeat ? " R" : ""); | ||||||
|     } else { |     } 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( |         snprintf( | ||||||
|             irda_monitor->display_text, |             irda_monitor->display_text, | ||||||
|             sizeof(irda_monitor->display_text), |             sizeof(irda_monitor->display_text), | ||||||
|                         "unknown data"); |             "RAW\n%d samples\n", | ||||||
|                     view_port_update(view_port); |             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("{"); |         printf("\r\n"); | ||||||
|                 while(print_counter != counter) { |  | ||||||
|                     printf("%lu, ", delays->timing[print_counter].duration); |  | ||||||
|                     ++print_counter; |  | ||||||
|                     if(print_counter >= IRDA_TIMINGS_SIZE) { |  | ||||||
|                         print_counter = 0; |  | ||||||
|     } |     } | ||||||
|                 } | } | ||||||
|                 printf("\r\n};\r\n"); | 
 | ||||||
|  | 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_worker_stop(irda_monitor->worker); | ||||||
|     irda_free_decoder(irda_monitor->handler); |     irda_worker_free(irda_monitor->worker); | ||||||
|     osMessageQueueDelete(irda_monitor->event_queue); |     osMessageQueueDelete(irda_monitor->event_queue); | ||||||
|     view_port_enabled_set(view_port, false); |     view_port_enabled_set(irda_monitor->view_port, false); | ||||||
|     gui_remove_view_port(gui, view_port); |     gui_remove_view_port(gui, irda_monitor->view_port); | ||||||
|     view_port_free(view_port); |     view_port_free(irda_monitor->view_port); | ||||||
|     furi_record_close("notification"); |  | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     free(irda_monitor); |     free(irda_monitor); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,11 +9,18 @@ extern "C" { | |||||||
| /**
 | /**
 | ||||||
|  * Signature of callback function for receiving continuous IRDA rx signal. |  * Signature of callback function for receiving continuous IRDA rx signal. | ||||||
|  * |  * | ||||||
|  * @param   level - level of input IRDA rx signal |  * @param   ctx[in] - context to pass to callback | ||||||
|  * @param   duration - duration of continuous rx signal level in us |  * @param   level[in] - level of input IRDA rx signal | ||||||
|  |  * @param   duration[in] - duration of continuous rx signal level in us | ||||||
|  */ |  */ | ||||||
| typedef void (*TimerISRCallback)(void* ctx, bool level, uint32_t duration); | typedef void (*ApiHalIrdaCaptureCallback)(void* ctx, bool level, uint32_t duration); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Signature of callback function for reaching silence timeout on IRDA port. | ||||||
|  |  * | ||||||
|  |  * @param   ctx[in] - context to pass to callback | ||||||
|  |  */ | ||||||
|  | typedef void (*ApiHalIrdaTimeoutCallback)(void* ctx); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Initialize IRDA RX timer to receive interrupts. |  * Initialize IRDA RX timer to receive interrupts. | ||||||
| @ -27,20 +34,37 @@ void api_hal_irda_rx_irq_init(void); | |||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_irq_deinit(void); | void api_hal_irda_rx_irq_deinit(void); | ||||||
| 
 | 
 | ||||||
|  | /** Setup api hal for receiving silence timeout.
 | ||||||
|  |  * Should be used with 'api_hal_irda_timeout_irq_set_callback()'. | ||||||
|  |  * | ||||||
|  |  * @param[in]   timeout_ms - time to wait for silence on IRDA port | ||||||
|  |  *                           before generating IRQ. | ||||||
|  |  */ | ||||||
|  | void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Setup callback for previously initialized IRDA RX interrupt. |  * Setup callback for previously initialized IRDA RX interrupt. | ||||||
|  * |  * | ||||||
|  * @param   callback - callback to call when RX signal edge changing occurs |  * @param[in]   callback - callback to call when RX signal edge changing occurs | ||||||
|  * @param   ctx - context for callback |  * @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 |  * Start generating IRDA TX PWM. Provides PWM initialization on | ||||||
|  * defined frequency. |  * defined frequency. | ||||||
|  * |  * | ||||||
|  * @param   duty_cycle - duty cycle |  * @param[in]   duty_cycle - duty cycle | ||||||
|  * @param   freq - PWM frequency to generate |  * @param[in]   freq - PWM frequency to generate | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_pwm_set(float duty_cycle, float freq); | void api_hal_irda_pwm_set(float duty_cycle, float freq); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,82 +1,58 @@ | |||||||
| #include "cmsis_os.h" |  | ||||||
| #include "api-hal-tim_i.h" |  | ||||||
| #include "api-hal-irda.h" | #include "api-hal-irda.h" | ||||||
|  | #include <cmsis_os2.h> | ||||||
|  | #include <api-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
| #include <stm32wbxx_ll_gpio.h> | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "main.h" | #include <main.h> | ||||||
| #include "api-hal-pwm.h" | #include <api-hal-pwm.h> | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| static struct{ | static struct{ | ||||||
|     TimerISRCallback callback; |     ApiHalIrdaCaptureCallback capture_callback; | ||||||
|     void *ctx; |     void *capture_context; | ||||||
|  |     ApiHalIrdaTimeoutCallback timeout_callback; | ||||||
|  |     void *timeout_context; | ||||||
| } timer_irda; | } timer_irda; | ||||||
| 
 | 
 | ||||||
|  | typedef enum{ | ||||||
|  |     TimerIRQSourceCCI1, | ||||||
|  |     TimerIRQSourceCCI2, | ||||||
|  | } TimerIRQSource; | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_tim_isr(TimerIRQSource source) | void api_hal_irda_rx_irq_init(void) { | ||||||
| { |  | ||||||
|     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 */ |  | ||||||
|     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); | ||||||
| 
 |  | ||||||
|     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||||
|     /**TIM2 GPIO Configuration
 |  | ||||||
|     PA0   ------> TIM2_CH1 |  | ||||||
|     */ |  | ||||||
|     GPIO_InitStruct.Pin = LL_GPIO_PIN_0; |  | ||||||
|     GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; |  | ||||||
|     GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; |  | ||||||
|     GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; |  | ||||||
|     GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; |  | ||||||
|     GPIO_InitStruct.Alternate = LL_GPIO_AF_1; |  | ||||||
|     LL_GPIO_Init(GPIOA, &GPIO_InitStruct); |  | ||||||
| 
 | 
 | ||||||
|  |     hal_gpio_init_ex(&gpio_irda_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); | ||||||
|  | 
 | ||||||
|  |     LL_TIM_InitTypeDef TIM_InitStruct = {0}; | ||||||
|     TIM_InitStruct.Prescaler = 64 - 1; |     TIM_InitStruct.Prescaler = 64 - 1; | ||||||
|     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; |     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; | ||||||
|     TIM_InitStruct.Autoreload = 0xFFFFFFFF; |     TIM_InitStruct.Autoreload = 0x7FFFFFFE; | ||||||
|     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; |     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; | ||||||
|     LL_TIM_Init(TIM2, &TIM_InitStruct); |     LL_TIM_Init(TIM2, &TIM_InitStruct); | ||||||
|  | 
 | ||||||
|     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); |     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); | ||||||
|     LL_TIM_EnableARRPreload(TIM2); |     LL_TIM_DisableARRPreload(TIM2); | ||||||
|  |     LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1); | ||||||
|  |     LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); | ||||||
|  |     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
|  |     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); | ||||||
|  |     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); | ||||||
|  |     LL_TIM_DisableIT_TRIG(TIM2); | ||||||
|  |     LL_TIM_DisableDMAReq_TRIG(TIM2); | ||||||
|     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); |     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); | ||||||
|     LL_TIM_DisableMasterSlaveMode(TIM2); |     LL_TIM_EnableMasterSlaveMode(TIM2); | ||||||
|     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); |     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); | ||||||
|     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); |     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); | ||||||
|     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); |     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); | ||||||
|     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); |     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); | ||||||
|     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); |     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); | ||||||
|     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); |     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); | ||||||
|     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); |  | ||||||
|     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); |  | ||||||
| 
 | 
 | ||||||
|     LL_TIM_EnableIT_CC1(TIM2); |     LL_TIM_EnableIT_CC1(TIM2); | ||||||
|     LL_TIM_EnableIT_CC2(TIM2); |     LL_TIM_EnableIT_CC2(TIM2); | ||||||
| @ -90,22 +66,30 @@ void api_hal_irda_rx_irq_init(void) | |||||||
|     NVIC_EnableIRQ(TIM2_IRQn); |     NVIC_EnableIRQ(TIM2_IRQn); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Doesn't work. F5 deprecated. */ | ||||||
| void api_hal_irda_rx_irq_deinit(void) { | void api_hal_irda_rx_irq_deinit(void) { | ||||||
|     LL_TIM_DisableIT_CC1(TIM2); |     LL_TIM_DeInit(TIM2); | ||||||
|     LL_TIM_DisableIT_CC2(TIM2); | } | ||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH1); | 
 | ||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms) { | ||||||
|  |     LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); | ||||||
|  |     LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); | ||||||
|  |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); | ||||||
|  |     LL_TIM_EnableIT_CC3(TIM2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool api_hal_irda_rx_irq_is_busy(void) { | bool api_hal_irda_rx_irq_is_busy(void) { | ||||||
|     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); |     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx) { | ||||||
|     furi_check(callback); |     timer_irda.capture_callback = callback; | ||||||
|  |     timer_irda.capture_context = ctx; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     timer_irda.callback = callback; | void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx) { | ||||||
|     timer_irda.ctx = ctx; |     timer_irda.timeout_callback = callback; | ||||||
|  |     timer_irda.timeout_context = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_pwm_set(float value, float freq) { | void api_hal_irda_pwm_set(float value, float freq) { | ||||||
| @ -115,4 +99,3 @@ void api_hal_irda_pwm_set(float value, float freq) { | |||||||
| void api_hal_irda_pwm_stop() { | void api_hal_irda_pwm_stop() { | ||||||
|     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); |     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -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_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||||
| const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||||
| const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; | ||||||
|  | const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -87,6 +87,9 @@ extern const GpioPin gpio_rfid_pull; | |||||||
| extern const GpioPin gpio_rfid_carrier_out; | extern const GpioPin gpio_rfid_carrier_out; | ||||||
| extern const GpioPin gpio_rfid_data_in; | extern const GpioPin gpio_rfid_data_in; | ||||||
| 
 | 
 | ||||||
|  | extern const GpioPin gpio_irda_rx; | ||||||
|  | extern const GpioPin gpio_irda_tx; | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ void TIM2_IRQHandler(void) | |||||||
| 
 | 
 | ||||||
|             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { |             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { | ||||||
|                 // input capture
 |                 // input capture
 | ||||||
|                 api_hal_irda_tim_isr(TimerIRQSourceCCI1); |  | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
| @ -30,7 +29,6 @@ void TIM2_IRQHandler(void) | |||||||
| 
 | 
 | ||||||
|             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { |             if (READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { | ||||||
|                 // input capture
 |                 // input capture
 | ||||||
|                 api_hal_irda_tim_isr(TimerIRQSourceCCI2); |  | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|  | |||||||
| @ -1,17 +1,22 @@ | |||||||
| #include "api-hal-interrupt.h" |  | ||||||
| #include "api-hal-irda.h" | #include "api-hal-irda.h" | ||||||
|  | #include <cmsis_os2.h> | ||||||
|  | #include <api-hal-interrupt.h> | ||||||
|  | #include <api-hal-resources.h> | ||||||
| 
 | 
 | ||||||
|  | #include <stdint.h> | ||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
| #include <stm32wbxx_ll_gpio.h> | #include <stm32wbxx_ll_gpio.h> | ||||||
| 
 | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "main.h" | #include <main.h> | ||||||
| #include "api-hal-pwm.h" | #include <api-hal-pwm.h> | ||||||
| 
 | 
 | ||||||
| static struct{ | static struct{ | ||||||
|     TimerISRCallback callback; |     ApiHalIrdaCaptureCallback capture_callback; | ||||||
|     void *ctx; |     void *capture_context; | ||||||
|  |     ApiHalIrdaTimeoutCallback timeout_callback; | ||||||
|  |     void *timeout_context; | ||||||
| } timer_irda; | } timer_irda; | ||||||
| 
 | 
 | ||||||
| typedef enum{ | typedef enum{ | ||||||
| @ -19,107 +24,105 @@ typedef enum{ | |||||||
|     TimerIRQSourceCCI2, |     TimerIRQSourceCCI2, | ||||||
| } TimerIRQSource; | } TimerIRQSource; | ||||||
| 
 | 
 | ||||||
| static void api_hal_irda_handle_capture(TimerIRQSource source) | static void api_hal_irda_handle_timeout(void) { | ||||||
| { |     /* Timers CNT register starts to counting from 0 to ARR, but it is
 | ||||||
|  |      * reseted when Channel 1 catches interrupt. It is not reseted by | ||||||
|  |      * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR). | ||||||
|  |      * This can cause false timeout: when time is over, but we started | ||||||
|  |      * receiving new signal few microseconds ago, because CNT register | ||||||
|  |      * is reseted once per period, not per sample. */ | ||||||
|  |     if (LL_GPIO_IsInputPinSet(gpio_irda_rx.port, gpio_irda_rx.pin) == 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (timer_irda.timeout_callback) | ||||||
|  |         timer_irda.timeout_callback(timer_irda.timeout_context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* High pin level is a Space state of IRDA signal. Invert level for further processing. */ | ||||||
|  | static void api_hal_irda_handle_capture(TimerIRQSource source) { | ||||||
|     uint32_t duration = 0; |     uint32_t duration = 0; | ||||||
|     bool level = 0; |     bool level = 0; | ||||||
| 
 | 
 | ||||||
|     switch (source) { |     switch (source) { | ||||||
|     case TimerIRQSourceCCI1: |     case TimerIRQSourceCCI1: | ||||||
|         duration = LL_TIM_OC_GetCompareCH1(TIM2); |         duration = LL_TIM_IC_GetCaptureCH1(TIM2) - LL_TIM_IC_GetCaptureCH2(TIM2); | ||||||
|         LL_TIM_SetCounter(TIM2, 0); |         level = 1; | ||||||
|         level = 0; |  | ||||||
|         break; |         break; | ||||||
|     case TimerIRQSourceCCI2: |     case TimerIRQSourceCCI2: | ||||||
|         duration = LL_TIM_OC_GetCompareCH2(TIM2); |         duration = LL_TIM_IC_GetCaptureCH2(TIM2); | ||||||
|         LL_TIM_SetCounter(TIM2, 0); |         level = 0; | ||||||
|         level = 1; |  | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         furi_check(0); |         furi_check(0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (timer_irda.callback) |     if (timer_irda.capture_callback) | ||||||
|         timer_irda.callback(timer_irda.ctx, level, duration); |         timer_irda.capture_callback(timer_irda.capture_context, level, duration); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void api_hal_irda_isr() { | static void api_hal_irda_isr() { | ||||||
|     if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) { |     if(LL_TIM_IsActiveFlag_CC3(TIM2)) { | ||||||
|  |         LL_TIM_ClearFlag_CC3(TIM2); | ||||||
|  |         api_hal_irda_handle_timeout(); | ||||||
|  |     } | ||||||
|  |     if(LL_TIM_IsActiveFlag_CC1(TIM2)) { | ||||||
|         LL_TIM_ClearFlag_CC1(TIM2); |         LL_TIM_ClearFlag_CC1(TIM2); | ||||||
| 
 | 
 | ||||||
|         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { |         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { | ||||||
|             // input capture
 |             // input capture
 | ||||||
|             api_hal_irda_handle_capture(TimerIRQSourceCCI1); |             api_hal_irda_handle_capture(TimerIRQSourceCCI1); | ||||||
|         } else { |  | ||||||
|             // output compare
 |  | ||||||
|             //  HAL_TIM_OC_DelayElapsedCallback(htim);
 |  | ||||||
|             //  HAL_TIM_PWM_PulseFinishedCallback(htim);
 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) { |     if(LL_TIM_IsActiveFlag_CC2(TIM2)) { | ||||||
|         LL_TIM_ClearFlag_CC2(TIM2); |         LL_TIM_ClearFlag_CC2(TIM2); | ||||||
| 
 | 
 | ||||||
|         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { |         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { | ||||||
|             // input capture
 |             // input capture
 | ||||||
|             api_hal_irda_handle_capture(TimerIRQSourceCCI2); |             api_hal_irda_handle_capture(TimerIRQSourceCCI2); | ||||||
|         } else { |  | ||||||
|             // output compare
 |  | ||||||
|             //  HAL_TIM_OC_DelayElapsedCallback(htim);
 |  | ||||||
|             //  HAL_TIM_PWM_PulseFinishedCallback(htim);
 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_init(void) | void api_hal_irda_rx_irq_init(void) { | ||||||
| { |  | ||||||
|     LL_TIM_InitTypeDef TIM_InitStruct = {0}; |  | ||||||
| 
 |  | ||||||
|     LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; |  | ||||||
| 
 |  | ||||||
|     /* Peripheral clock enable */ |  | ||||||
|     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); | ||||||
| 
 |  | ||||||
|     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||||
|     /**TIM2 GPIO Configuration
 |  | ||||||
|     PA0   ------> TIM2_CH1 |  | ||||||
|     */ |  | ||||||
|     GPIO_InitStruct.Pin = LL_GPIO_PIN_0; |  | ||||||
|     GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; |  | ||||||
|     GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; |  | ||||||
|     GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; |  | ||||||
|     GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; |  | ||||||
|     GPIO_InitStruct.Alternate = LL_GPIO_AF_1; |  | ||||||
|     LL_GPIO_Init(GPIOA, &GPIO_InitStruct); |  | ||||||
| 
 | 
 | ||||||
|  |     hal_gpio_init_ex(&gpio_irda_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); | ||||||
|  | 
 | ||||||
|  |     LL_TIM_InitTypeDef TIM_InitStruct = {0}; | ||||||
|     TIM_InitStruct.Prescaler = 64 - 1; |     TIM_InitStruct.Prescaler = 64 - 1; | ||||||
|     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; |     TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; | ||||||
|     TIM_InitStruct.Autoreload = 0xFFFFFFFF; |     TIM_InitStruct.Autoreload = 0x7FFFFFFE; | ||||||
|     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; |     TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; | ||||||
|     LL_TIM_Init(TIM2, &TIM_InitStruct); |     LL_TIM_Init(TIM2, &TIM_InitStruct); | ||||||
|  | 
 | ||||||
|     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); |     LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); | ||||||
|     LL_TIM_EnableARRPreload(TIM2); |     LL_TIM_DisableARRPreload(TIM2); | ||||||
|  |     LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1); | ||||||
|  |     LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); | ||||||
|  |     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
|  |     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); | ||||||
|  |     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); | ||||||
|  |     LL_TIM_DisableIT_TRIG(TIM2); | ||||||
|  |     LL_TIM_DisableDMAReq_TRIG(TIM2); | ||||||
|     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); |     LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); | ||||||
|     LL_TIM_DisableMasterSlaveMode(TIM2); |     LL_TIM_EnableMasterSlaveMode(TIM2); | ||||||
|     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); |     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); | ||||||
|     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); |     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); | ||||||
|     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); |     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); | ||||||
|     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); |     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); | ||||||
|     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); |     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); | ||||||
|     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); |     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); | ||||||
|     LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); |  | ||||||
|     LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); |  | ||||||
| 
 | 
 | ||||||
|     LL_TIM_EnableIT_CC1(TIM2); |     LL_TIM_EnableIT_CC1(TIM2); | ||||||
|     LL_TIM_EnableIT_CC2(TIM2); |     LL_TIM_EnableIT_CC2(TIM2); | ||||||
|     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); | ||||||
|     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
| 
 | 
 | ||||||
|  |     api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr); | ||||||
|  | 
 | ||||||
|     LL_TIM_SetCounter(TIM2, 0); |     LL_TIM_SetCounter(TIM2, 0); | ||||||
|     LL_TIM_EnableCounter(TIM2); |     LL_TIM_EnableCounter(TIM2); | ||||||
| 
 |  | ||||||
|     api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr); |  | ||||||
|     NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); |  | ||||||
|     NVIC_EnableIRQ(TIM2_IRQn); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_deinit(void) { | void api_hal_irda_rx_irq_deinit(void) { | ||||||
| @ -127,15 +130,25 @@ void api_hal_irda_rx_irq_deinit(void) { | |||||||
|     api_hal_interrupt_set_timer_isr(TIM2, NULL); |     api_hal_interrupt_set_timer_isr(TIM2, NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms) { | ||||||
|  |     LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); | ||||||
|  |     LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); | ||||||
|  |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); | ||||||
|  |     LL_TIM_EnableIT_CC3(TIM2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool api_hal_irda_rx_irq_is_busy(void) { | bool api_hal_irda_rx_irq_is_busy(void) { | ||||||
|     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); |     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx) { | ||||||
|     furi_check(callback); |     timer_irda.capture_callback = callback; | ||||||
|  |     timer_irda.capture_context = ctx; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     timer_irda.callback = callback; | void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx) { | ||||||
|     timer_irda.ctx = ctx; |     timer_irda.timeout_callback = callback; | ||||||
|  |     timer_irda.timeout_context = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_pwm_set(float value, float freq) { | void api_hal_irda_pwm_set(float value, float freq) { | ||||||
|  | |||||||
| @ -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_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||||
| const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||||
| const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; | ||||||
|  | const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -86,6 +86,9 @@ extern const GpioPin gpio_rfid_pull; | |||||||
| extern const GpioPin gpio_rfid_carrier_out; | extern const GpioPin gpio_rfid_carrier_out; | ||||||
| extern const GpioPin gpio_rfid_data_in; | extern const GpioPin gpio_rfid_data_in; | ||||||
| 
 | 
 | ||||||
|  | extern const GpioPin gpio_irda_rx; | ||||||
|  | extern const GpioPin gpio_irda_tx; | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| #include "file_reader/file_reader.hpp" | #include <file_reader.h> | ||||||
| 
 | 
 | ||||||
| std::string FileReader::getline(File* file) { | std::string FileReader::getline(File* file) { | ||||||
|     std::string str; |     std::string str; | ||||||
|     size_t newline_index = 0; |     size_t newline_index = 0; | ||||||
|     bool found_eol = false; |     bool found_eol = false; | ||||||
|  |     bool max_length_exceeded = false; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         if(file_buf_cnt > 0) { |         if(file_buf_cnt > 0) { | ||||||
| @ -20,7 +21,12 @@ std::string FileReader::getline(File* file) { | |||||||
|                 furi_assert(0); |                 furi_assert(0); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             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); |                 str.append(file_buf, end_index); | ||||||
|  | 
 | ||||||
|             memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index); |             memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index); | ||||||
|             file_buf_cnt = file_buf_cnt - end_index; |             file_buf_cnt = file_buf_cnt - end_index; | ||||||
|             if(found_eol) break; |             if(found_eol) break; | ||||||
| @ -33,6 +39,9 @@ std::string FileReader::getline(File* file) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (max_length_exceeded) | ||||||
|  |         str.clear(); | ||||||
|  | 
 | ||||||
|     return str; |     return str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ class FileReader { | |||||||
| private: | private: | ||||||
|     char file_buf[48]; |     char file_buf[48]; | ||||||
|     size_t file_buf_cnt = 0; |     size_t file_buf_cnt = 0; | ||||||
|  |     size_t max_line_length = 0; | ||||||
|     SdCard_Api* sd_ex_api; |     SdCard_Api* sd_ex_api; | ||||||
|     FS_Api* fs_api; |     FS_Api* fs_api; | ||||||
| 
 | 
 | ||||||
| @ -35,5 +36,9 @@ public: | |||||||
|     FS_Api& get_fs_api() { |     FS_Api& get_fs_api() { | ||||||
|         return *fs_api; |         return *fs_api; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     void set_max_line_length(size_t value) { | ||||||
|  |         max_line_length = value; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -69,14 +69,6 @@ void irda_free_decoder(IrdaDecoderHandler* handler); | |||||||
|  */ |  */ | ||||||
| void irda_reset_decoder(IrdaDecoderHandler* handler); | void irda_reset_decoder(IrdaDecoderHandler* handler); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Send message over IRDA. |  | ||||||
|  * |  | ||||||
|  * \param[in]   message     - message to send. |  | ||||||
|  * \param[in]   times       - number of times message should be sent. |  | ||||||
|  */ |  | ||||||
| void irda_send(const IrdaMessage* message, int times); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Get protocol name by protocol enum. |  * Get protocol name by protocol enum. | ||||||
|  * |  * | ||||||
| @ -117,19 +109,6 @@ uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); | |||||||
|  */ |  */ | ||||||
| bool irda_is_protocol_valid(IrdaProtocol protocol); | bool irda_is_protocol_valid(IrdaProtocol protocol); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Send raw data through infrared port. |  | ||||||
|  * |  | ||||||
|  * \param[in]   protocol - use IRDA settings (duty cycle, frequency) from |  | ||||||
|  *              this protocol. If provided IrdaProtocolUnknown - use |  | ||||||
|  *              default settings. |  | ||||||
|  * \param[in]   timings - array of timings to send. |  | ||||||
|  * \param[in]   timings_cnt - timings array size. |  | ||||||
|  * \param[in]   start_from_mark - true if timings starts from mark, |  | ||||||
|  *              otherwise from space |  | ||||||
|  */ |  | ||||||
| void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Allocate IRDA encoder. |  * Allocate IRDA encoder. | ||||||
|  * |  * | ||||||
| @ -6,9 +6,11 @@ | |||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include <api-hal-delay.h> | #include <api-hal-delay.h> | ||||||
| 
 | 
 | ||||||
| static void irda_set_tx(uint32_t duration, bool level) { | #define IRDA_SET_TX_COMMON(d, l)        irda_set_tx((d), (l), IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY) | ||||||
|  | 
 | ||||||
|  | static void irda_set_tx(uint32_t duration, bool level, float duty_cycle, float frequency) { | ||||||
|     if (level) { |     if (level) { | ||||||
|         api_hal_irda_pwm_set(IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY); |         api_hal_irda_pwm_set(duty_cycle, frequency); | ||||||
|         delay_us(duration); |         delay_us(duration); | ||||||
|     } else { |     } else { | ||||||
|         api_hal_irda_pwm_stop(); |         api_hal_irda_pwm_stop(); | ||||||
| @ -16,12 +18,21 @@ static void irda_set_tx(uint32_t duration, bool level) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency) { | ||||||
|  |     __disable_irq(); | ||||||
|  |     for (uint32_t i = 0; i < timings_cnt; ++i) { | ||||||
|  |         irda_set_tx(timings[i], (i % 2) ^ start_from_mark, duty_cycle, frequency); | ||||||
|  |     } | ||||||
|  |     IRDA_SET_TX_COMMON(0, false); | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) { | void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) { | ||||||
|     __disable_irq(); |     __disable_irq(); | ||||||
|     for (uint32_t i = 0; i < timings_cnt; ++i) { |     for (uint32_t i = 0; i < timings_cnt; ++i) { | ||||||
|         irda_set_tx(timings[i], (i % 2) ^ start_from_mark); |         IRDA_SET_TX_COMMON(timings[i], (i % 2) ^ start_from_mark); | ||||||
|     } |     } | ||||||
|     irda_set_tx(0, false); |     IRDA_SET_TX_COMMON(0, false); | ||||||
|     __enable_irq(); |     __enable_irq(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -49,7 +60,7 @@ void irda_send(const IrdaMessage* message, int times) { | |||||||
|     while (times) { |     while (times) { | ||||||
|         status = irda_encode(handler, &duration, &level); |         status = irda_encode(handler, &duration, &level); | ||||||
|         if (status != IrdaStatusError) { |         if (status != IrdaStatusError) { | ||||||
|             irda_set_tx(duration, level); |             IRDA_SET_TX_COMMON(duration, level); | ||||||
|         } else { |         } else { | ||||||
|             furi_assert(0); |             furi_assert(0); | ||||||
|             break; |             break; | ||||||
| @ -58,7 +69,7 @@ void irda_send(const IrdaMessage* message, int times) { | |||||||
|             --times; |             --times; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     irda_set_tx(0, false); |     IRDA_SET_TX_COMMON(0, false); | ||||||
|     __enable_irq(); |     __enable_irq(); | ||||||
| 
 | 
 | ||||||
|     irda_free_encoder(handler); |     irda_free_encoder(handler); | ||||||
							
								
								
									
										41
									
								
								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) | CPP_SOURCES		+= $(wildcard $(LIB_DIR)/file_reader/*.cpp) | ||||||
| 
 | 
 | ||||||
| #irda lib
 | #irda lib
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/irda | CFLAGS			+= -I$(LIB_DIR)/irda/encoder_decoder | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*.c) | CFLAGS			+= -I$(LIB_DIR)/irda/worker | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*/*.c) | C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/encoder_decoder/*.c) | ||||||
|  | C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/encoder_decoder/*/*.c) | ||||||
|  | C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/worker/*.c) | ||||||
| 
 | 
 | ||||||
| #args lib
 | #args lib
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/args | CFLAGS			+= -I$(LIB_DIR)/args | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov