infrared_signal.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include "infrared_signal.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <core/check.h>
  5. #include <lib/infrared/worker/infrared_transmit.h>
  6. #include <lib/infrared/worker/infrared_worker.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 (%X - %X): %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 - %X): %X",
  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. FuriString* buf;
  87. buf = furi_string_alloc();
  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(furi_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. furi_string_free(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. static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {
  120. FuriString* tmp = furi_string_alloc();
  121. bool success = false;
  122. do {
  123. if(!flipper_format_read_string(ff, "type", tmp)) break;
  124. if(furi_string_equal(tmp, "raw")) {
  125. success = infrared_signal_read_raw(signal, ff);
  126. } else if(furi_string_equal(tmp, "parsed")) {
  127. success = infrared_signal_read_message(signal, ff);
  128. } else {
  129. FURI_LOG_E(TAG, "Unknown signal type");
  130. }
  131. } while(false);
  132. furi_string_free(tmp);
  133. return success;
  134. }
  135. InfraredSignal* infrared_signal_alloc(void) {
  136. InfraredSignal* signal = malloc(sizeof(InfraredSignal));
  137. signal->is_raw = false;
  138. signal->payload.message.protocol = InfraredProtocolUnknown;
  139. return signal;
  140. }
  141. void infrared_signal_free(InfraredSignal* signal) {
  142. infrared_signal_clear_timings(signal);
  143. free(signal);
  144. }
  145. bool infrared_signal_is_raw(InfraredSignal* signal) {
  146. return signal->is_raw;
  147. }
  148. bool infrared_signal_is_valid(InfraredSignal* signal) {
  149. return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
  150. infrared_signal_is_message_valid(&signal->payload.message);
  151. }
  152. void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
  153. if(other->is_raw) {
  154. const InfraredRawSignal* raw = &other->payload.raw;
  155. infrared_signal_set_raw_signal(
  156. signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
  157. } else {
  158. const InfraredMessage* message = &other->payload.message;
  159. infrared_signal_set_message(signal, message);
  160. }
  161. }
  162. void infrared_signal_set_raw_signal(
  163. InfraredSignal* signal,
  164. const uint32_t* timings,
  165. size_t timings_size,
  166. uint32_t frequency,
  167. float duty_cycle) {
  168. infrared_signal_clear_timings(signal);
  169. signal->is_raw = true;
  170. signal->payload.raw.timings_size = timings_size;
  171. signal->payload.raw.frequency = frequency;
  172. signal->payload.raw.duty_cycle = duty_cycle;
  173. signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
  174. memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
  175. }
  176. InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
  177. furi_assert(signal->is_raw);
  178. return &signal->payload.raw;
  179. }
  180. void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
  181. infrared_signal_clear_timings(signal);
  182. signal->is_raw = false;
  183. signal->payload.message = *message;
  184. }
  185. InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
  186. furi_assert(!signal->is_raw);
  187. return &signal->payload.message;
  188. }
  189. bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
  190. if(!flipper_format_write_comment_cstr(ff, "") ||
  191. !flipper_format_write_string_cstr(ff, "name", name)) {
  192. return false;
  193. } else if(signal->is_raw) {
  194. return infrared_signal_save_raw(&signal->payload.raw, ff);
  195. } else {
  196. return infrared_signal_save_message(&signal->payload.message, ff);
  197. }
  198. }
  199. bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
  200. FuriString* tmp = furi_string_alloc();
  201. bool success = false;
  202. do {
  203. if(!flipper_format_read_string(ff, "name", tmp)) break;
  204. furi_string_set(name, tmp);
  205. if(!infrared_signal_read_body(signal, ff)) break;
  206. success = true;
  207. } while(0);
  208. furi_string_free(tmp);
  209. return success;
  210. }
  211. bool infrared_signal_search_and_read(
  212. InfraredSignal* signal,
  213. FlipperFormat* ff,
  214. const FuriString* name) {
  215. bool success = false;
  216. FuriString* tmp = furi_string_alloc();
  217. do {
  218. bool is_name_found = false;
  219. while(flipper_format_read_string(ff, "name", tmp)) {
  220. is_name_found = furi_string_equal(name, tmp);
  221. if(is_name_found) break;
  222. }
  223. if(!is_name_found) break;
  224. if(!infrared_signal_read_body(signal, ff)) break;
  225. success = true;
  226. } while(false);
  227. furi_string_free(tmp);
  228. return success;
  229. }
  230. void infrared_signal_transmit(InfraredSignal* signal) {
  231. if(signal->is_raw) {
  232. InfraredRawSignal* raw_signal = &signal->payload.raw;
  233. infrared_send_raw_ext(
  234. raw_signal->timings,
  235. raw_signal->timings_size,
  236. true,
  237. raw_signal->frequency,
  238. raw_signal->duty_cycle);
  239. } else {
  240. InfraredMessage* message = &signal->payload.message;
  241. infrared_send(message, 2);
  242. }
  243. }