infrared_signal.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include "infrared_signal.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <furi/check.h>
  5. #include <infrared_worker.h>
  6. #include <infrared_transmit.h>
  7. #define TAG "InfraredSignal"
  8. struct InfraredSignal {
  9. bool is_raw;
  10. union {
  11. InfraredMessage message;
  12. InfraredRawSignal raw;
  13. } payload;
  14. };
  15. static void infrared_signal_clear_timings(InfraredSignal* signal) {
  16. if(signal->is_raw) {
  17. free(signal->payload.raw.timings);
  18. signal->payload.raw.timings_size = 0;
  19. signal->payload.raw.timings = NULL;
  20. }
  21. }
  22. static bool infrared_signal_is_message_valid(InfraredMessage* message) {
  23. if(!infrared_is_protocol_valid(message->protocol)) {
  24. FURI_LOG_E(TAG, "Unknown protocol");
  25. return false;
  26. }
  27. uint32_t address_length = infrared_get_protocol_address_length(message->protocol);
  28. uint32_t address_mask = (1UL << address_length) - 1;
  29. if(message->address != (message->address & address_mask)) {
  30. FURI_LOG_E(
  31. TAG,
  32. "Address is out of range (mask 0x%08lX): 0x%lX\r\n",
  33. address_mask,
  34. message->address);
  35. return false;
  36. }
  37. uint32_t command_length = infrared_get_protocol_command_length(message->protocol);
  38. uint32_t command_mask = (1UL << command_length) - 1;
  39. if(message->command != (message->command & command_mask)) {
  40. FURI_LOG_E(
  41. TAG,
  42. "Command is out of range (mask 0x%08lX): 0x%lX\r\n",
  43. command_mask,
  44. message->command);
  45. return false;
  46. }
  47. return true;
  48. }
  49. static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
  50. if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
  51. FURI_LOG_E(
  52. TAG,
  53. "Frequency is out of range (%lX - %lX): %lX",
  54. INFRARED_MIN_FREQUENCY,
  55. INFRARED_MAX_FREQUENCY,
  56. raw->frequency);
  57. return false;
  58. } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) {
  59. FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle);
  60. return false;
  61. } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {
  62. FURI_LOG_E(
  63. TAG,
  64. "Timings amount is out of range (0 - %lX): %lX",
  65. MAX_TIMINGS_AMOUNT,
  66. raw->timings_size);
  67. return false;
  68. }
  69. return true;
  70. }
  71. static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
  72. const char* protocol_name = infrared_get_protocol_name(message->protocol);
  73. return flipper_format_write_string_cstr(ff, "type", "parsed") &&
  74. flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
  75. flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) &&
  76. flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
  77. }
  78. static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
  79. furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
  80. return flipper_format_write_string_cstr(ff, "type", "raw") &&
  81. flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
  82. flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) &&
  83. flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size);
  84. }
  85. static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {
  86. string_t buf;
  87. string_init(buf);
  88. bool success = false;
  89. do {
  90. if(!flipper_format_read_string(ff, "protocol", buf)) break;
  91. InfraredMessage message;
  92. message.protocol = infrared_get_protocol_by_name(string_get_cstr(buf));
  93. success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) &&
  94. flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) &&
  95. infrared_signal_is_message_valid(&message);
  96. if(!success) break;
  97. infrared_signal_set_message(signal, &message);
  98. } while(0);
  99. string_clear(buf);
  100. return success;
  101. }
  102. static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {
  103. uint32_t timings_size, frequency;
  104. float duty_cycle;
  105. bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) &&
  106. flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) &&
  107. flipper_format_get_value_count(ff, "data", &timings_size);
  108. if(!success || timings_size > MAX_TIMINGS_AMOUNT) {
  109. return false;
  110. }
  111. uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);
  112. success = flipper_format_read_uint32(ff, "data", timings, timings_size);
  113. if(success) {
  114. infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
  115. }
  116. free(timings);
  117. return success;
  118. }
  119. InfraredSignal* infrared_signal_alloc() {
  120. InfraredSignal* signal = malloc(sizeof(InfraredSignal));
  121. signal->is_raw = false;
  122. signal->payload.message.protocol = InfraredProtocolUnknown;
  123. return signal;
  124. }
  125. void infrared_signal_free(InfraredSignal* signal) {
  126. infrared_signal_clear_timings(signal);
  127. free(signal);
  128. }
  129. bool infrared_signal_is_raw(InfraredSignal* signal) {
  130. return signal->is_raw;
  131. }
  132. bool infrared_signal_is_valid(InfraredSignal* signal) {
  133. return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
  134. infrared_signal_is_message_valid(&signal->payload.message);
  135. }
  136. void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
  137. if(other->is_raw) {
  138. const InfraredRawSignal* raw = &other->payload.raw;
  139. infrared_signal_set_raw_signal(
  140. signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
  141. } else {
  142. const InfraredMessage* message = &other->payload.message;
  143. infrared_signal_set_message(signal, message);
  144. }
  145. }
  146. void infrared_signal_set_raw_signal(
  147. InfraredSignal* signal,
  148. const uint32_t* timings,
  149. size_t timings_size,
  150. uint32_t frequency,
  151. float duty_cycle) {
  152. infrared_signal_clear_timings(signal);
  153. signal->is_raw = true;
  154. signal->payload.raw.timings_size = timings_size;
  155. signal->payload.raw.frequency = frequency;
  156. signal->payload.raw.duty_cycle = duty_cycle;
  157. signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
  158. memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
  159. }
  160. InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
  161. furi_assert(signal->is_raw);
  162. return &signal->payload.raw;
  163. }
  164. void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
  165. infrared_signal_clear_timings(signal);
  166. signal->is_raw = false;
  167. signal->payload.message = *message;
  168. }
  169. InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
  170. furi_assert(!signal->is_raw);
  171. return &signal->payload.message;
  172. }
  173. bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
  174. if(!flipper_format_write_comment_cstr(ff, "") ||
  175. !flipper_format_write_string_cstr(ff, "name", name)) {
  176. return false;
  177. } else if(signal->is_raw) {
  178. return infrared_signal_save_raw(&signal->payload.raw, ff);
  179. } else {
  180. return infrared_signal_save_message(&signal->payload.message, ff);
  181. }
  182. }
  183. bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) {
  184. string_t buf;
  185. string_init(buf);
  186. bool success = false;
  187. do {
  188. if(!flipper_format_read_string(ff, "name", buf)) break;
  189. string_set(name, buf);
  190. if(!flipper_format_read_string(ff, "type", buf)) break;
  191. if(!string_cmp_str(buf, "raw")) {
  192. success = infrared_signal_read_raw(signal, ff);
  193. } else if(!string_cmp_str(buf, "parsed")) {
  194. success = infrared_signal_read_message(signal, ff);
  195. } else {
  196. FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) ");
  197. }
  198. } while(0);
  199. string_clear(buf);
  200. return success;
  201. }
  202. void infrared_signal_transmit(InfraredSignal* signal) {
  203. if(signal->is_raw) {
  204. InfraredRawSignal* raw_signal = &signal->payload.raw;
  205. infrared_send_raw_ext(
  206. raw_signal->timings,
  207. raw_signal->timings_size,
  208. true,
  209. raw_signal->frequency,
  210. raw_signal->duty_cycle);
  211. } else {
  212. InfraredMessage* message = &signal->payload.message;
  213. infrared_send(message, 1);
  214. }
  215. }