action_ir.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Methods for IR transmission
  2. // infrared
  3. #include <infrared.h>
  4. #include <infrared/encoder_decoder/infrared.h>
  5. #include <infrared/worker/infrared_transmit.h>
  6. #include <infrared/worker/infrared_worker.h>
  7. #include <flipper_format/flipper_format.h>
  8. #include "action_i.h"
  9. #include "quac.h"
  10. #define INFRARED_FILE_TYPE "IR signals file"
  11. #define INFRARED_FILE_VERSION 1
  12. typedef struct {
  13. size_t timings_size; /**< Number of elements in the timings array. */
  14. uint32_t* timings; /**< Pointer to an array of timings describing the signal. */
  15. uint32_t frequency; /**< Carrier frequency of the signal. */
  16. float duty_cycle; /**< Duty cycle of the signal. */
  17. } InfraredRawSignal;
  18. typedef struct InfraredSignal {
  19. bool is_raw;
  20. union {
  21. InfraredMessage message; // protocol, address, command, repeat
  22. InfraredRawSignal raw;
  23. } payload;
  24. } InfraredSignal;
  25. InfraredSignal* infrared_signal_alloc() {
  26. InfraredSignal* signal = malloc(sizeof(InfraredSignal));
  27. signal->is_raw = false;
  28. signal->payload.message.protocol = InfraredProtocolUnknown;
  29. return signal;
  30. }
  31. void infrared_signal_free(InfraredSignal* signal) {
  32. if(signal->is_raw) {
  33. free(signal->payload.raw.timings);
  34. signal->payload.raw.timings = NULL;
  35. }
  36. free(signal);
  37. }
  38. void action_ir_tx(void* context, const FuriString* action_path, FuriString* error) {
  39. UNUSED(error);
  40. App* app = context;
  41. const char* file_name = furi_string_get_cstr(action_path);
  42. InfraredSignal* signal = infrared_signal_alloc();
  43. FlipperFormat* fff_data_file = flipper_format_file_alloc(app->storage);
  44. FuriString* temp_str;
  45. temp_str = furi_string_alloc();
  46. uint32_t temp_data32;
  47. // https://developer.flipper.net/flipperzero/doxygen/infrared_file_format.html
  48. // TODO: Right now we only read the first signal found in the file. Add support
  49. // for reading any signal by 'name'
  50. do {
  51. if(!flipper_format_file_open_existing(fff_data_file, file_name)) {
  52. ACTION_SET_ERROR("IR: Error opening %s", file_name);
  53. break;
  54. }
  55. if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
  56. ACTION_SET_ERROR("IR: Missing or incorrect header");
  57. break;
  58. }
  59. if(!furi_string_cmp_str(temp_str, INFRARED_FILE_TYPE) &&
  60. temp_data32 == INFRARED_FILE_VERSION) {
  61. } else {
  62. ACTION_SET_ERROR("IR: File type or version mismatch");
  63. break;
  64. }
  65. if(!flipper_format_read_string(fff_data_file, "name", temp_str)) {
  66. ACTION_SET_ERROR("IR: Invalid or missing name");
  67. break;
  68. }
  69. // FURI_LOG_I(TAG, "Reading signal %s", furi_string_get_cstr(temp_str));
  70. if(!flipper_format_read_string(fff_data_file, "type", temp_str)) {
  71. ACTION_SET_ERROR("IR: Type missing");
  72. break;
  73. }
  74. if(!furi_string_cmp_str(temp_str, "parsed")) {
  75. // FURI_LOG_I(TAG, "IR File is PARSED");
  76. signal->is_raw = false;
  77. if(!flipper_format_read_string(fff_data_file, "protocol", temp_str)) {
  78. ACTION_SET_ERROR("IR: Invalid or missing protocol");
  79. break;
  80. }
  81. signal->payload.message.protocol =
  82. infrared_get_protocol_by_name(furi_string_get_cstr(temp_str));
  83. if(!infrared_is_protocol_valid(signal->payload.message.protocol)) {
  84. ACTION_SET_ERROR("IR: Invalid or unknown protocol");
  85. break;
  86. }
  87. // Why do these methods exist, when the spec says the address and command
  88. // lengths MUST be 4 bytes?
  89. // uint8_t address_len;
  90. // address_len = infrared_get_protocol_address_length(signal->payload.message.protocol);
  91. // uint8_t command_len;
  92. // command_len = infrared_get_protocol_command_length(signal->payload.message.protocol);
  93. if(!flipper_format_read_hex(
  94. fff_data_file, "address", (uint8_t*)&signal->payload.message.address, 4)) {
  95. ACTION_SET_ERROR("IR: Failed to read address");
  96. break;
  97. }
  98. if(!flipper_format_read_hex(
  99. fff_data_file, "command", (uint8_t*)&signal->payload.message.command, 4)) {
  100. ACTION_SET_ERROR("IR: Failed to read command");
  101. break;
  102. }
  103. // FURI_LOG_I(
  104. // TAG,
  105. // "IR: Sending parsed => %s %lu %lu",
  106. // infrared_get_protocol_name(signal->payload.message.protocol),
  107. // signal->payload.message.address,
  108. // signal->payload.message.command);
  109. infrared_send(&signal->payload.message, 1);
  110. } else if(!furi_string_cmp_str(temp_str, "raw")) {
  111. // FURI_LOG_I(TAG, "IR File is RAW");
  112. signal->is_raw = true;
  113. if(!flipper_format_read_uint32(
  114. fff_data_file, "frequency", &signal->payload.raw.frequency, 1)) {
  115. ACTION_SET_ERROR("IR: Failed to read frequency");
  116. break;
  117. }
  118. if(!flipper_format_read_float(
  119. fff_data_file, "duty_cycle", &signal->payload.raw.duty_cycle, 1)) {
  120. ACTION_SET_ERROR("IR: Failed to read duty cycle");
  121. break;
  122. }
  123. if(!flipper_format_get_value_count(fff_data_file, "data", &temp_data32)) {
  124. ACTION_SET_ERROR("IR: Failed to get size of data");
  125. break;
  126. }
  127. if(temp_data32 > MAX_TIMINGS_AMOUNT) {
  128. ACTION_SET_ERROR("IR: Data size exceeds limit");
  129. break;
  130. }
  131. signal->payload.raw.timings_size = temp_data32;
  132. signal->payload.raw.timings =
  133. malloc(sizeof(uint32_t) * signal->payload.raw.timings_size);
  134. if(!flipper_format_read_uint32(
  135. fff_data_file, "data", signal->payload.raw.timings, temp_data32)) {
  136. ACTION_SET_ERROR("IR: Failed to read data");
  137. break;
  138. }
  139. // FURI_LOG_I(
  140. // TAG,
  141. // "IR: Sending raw => %d timings, %lu Hz, %f",
  142. // signal->payload.raw.timings_size,
  143. // signal->payload.raw.frequency,
  144. // (double)signal->payload.raw.duty_cycle);
  145. infrared_send_raw_ext(
  146. signal->payload.raw.timings,
  147. signal->payload.raw.timings_size,
  148. true,
  149. signal->payload.raw.frequency,
  150. signal->payload.raw.duty_cycle);
  151. } else {
  152. ACTION_SET_ERROR("IR: Unknown type: %s", furi_string_get_cstr(temp_str));
  153. break;
  154. }
  155. } while(false);
  156. furi_string_free(temp_str);
  157. flipper_format_free(fff_data_file);
  158. infrared_signal_free(signal);
  159. }