infrared_signal.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /*
  2. This file was taken from the project:
  3. https://github.com/DarkFlippers/unleashed-firmware
  4. The original project is licensed under the GNU GPLv3
  5. Modifications made:
  6. - Added function infrared_signal_transmit_times()
  7. */
  8. #include "infrared_signal.h"
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <core/check.h>
  12. #include <infrared_worker.h>
  13. #include <infrared_transmit.h>
  14. #define TAG "InfraredSignal"
  15. struct InfraredSignal {
  16. bool is_raw;
  17. union {
  18. InfraredMessage message;
  19. InfraredRawSignal raw;
  20. } payload;
  21. };
  22. static void infrared_signal_clear_timings(InfraredSignal* signal) {
  23. if(signal->is_raw) {
  24. free(signal->payload.raw.timings);
  25. signal->payload.raw.timings_size = 0;
  26. signal->payload.raw.timings = NULL;
  27. }
  28. }
  29. static bool infrared_signal_is_message_valid(InfraredMessage* message) {
  30. if(!infrared_is_protocol_valid(message->protocol)) {
  31. FURI_LOG_E(TAG, "Unknown protocol");
  32. return false;
  33. }
  34. uint32_t address_length = infrared_get_protocol_address_length(message->protocol);
  35. uint32_t address_mask = (1UL << address_length) - 1;
  36. if(message->address != (message->address & address_mask)) {
  37. FURI_LOG_E(
  38. TAG,
  39. "Address is out of range (mask 0x%08lX): 0x%lX\r\n",
  40. address_mask,
  41. message->address);
  42. return false;
  43. }
  44. uint32_t command_length = infrared_get_protocol_command_length(message->protocol);
  45. uint32_t command_mask = (1UL << command_length) - 1;
  46. if(message->command != (message->command & command_mask)) {
  47. FURI_LOG_E(
  48. TAG,
  49. "Command is out of range (mask 0x%08lX): 0x%lX\r\n",
  50. command_mask,
  51. message->command);
  52. return false;
  53. }
  54. return true;
  55. }
  56. static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
  57. if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
  58. FURI_LOG_E(
  59. TAG,
  60. "Frequency is out of range (%X - %X): %lX",
  61. INFRARED_MIN_FREQUENCY,
  62. INFRARED_MAX_FREQUENCY,
  63. raw->frequency);
  64. return false;
  65. } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) {
  66. FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle);
  67. return false;
  68. } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {
  69. FURI_LOG_E(
  70. TAG,
  71. "Timings amount is out of range (0 - %X): %zX",
  72. MAX_TIMINGS_AMOUNT,
  73. raw->timings_size);
  74. return false;
  75. }
  76. return true;
  77. }
  78. static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
  79. const char* protocol_name = infrared_get_protocol_name(message->protocol);
  80. return flipper_format_write_string_cstr(ff, "type", "parsed") &&
  81. flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
  82. flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) &&
  83. flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
  84. }
  85. static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
  86. furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
  87. return flipper_format_write_string_cstr(ff, "type", "raw") &&
  88. flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
  89. flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) &&
  90. flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size);
  91. }
  92. static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {
  93. FuriString* buf;
  94. buf = furi_string_alloc();
  95. bool success = false;
  96. do {
  97. if(!flipper_format_read_string(ff, "protocol", buf)) break;
  98. InfraredMessage message;
  99. message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));
  100. success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) &&
  101. flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) &&
  102. infrared_signal_is_message_valid(&message);
  103. if(!success) break;
  104. infrared_signal_set_message(signal, &message);
  105. } while(0);
  106. furi_string_free(buf);
  107. return success;
  108. }
  109. static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {
  110. uint32_t timings_size, frequency;
  111. float duty_cycle;
  112. bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) &&
  113. flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) &&
  114. flipper_format_get_value_count(ff, "data", &timings_size);
  115. if(!success || timings_size > MAX_TIMINGS_AMOUNT) {
  116. return false;
  117. }
  118. uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);
  119. success = flipper_format_read_uint32(ff, "data", timings, timings_size);
  120. if(success) {
  121. infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
  122. }
  123. free(timings);
  124. return success;
  125. }
  126. static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {
  127. FuriString* tmp = furi_string_alloc();
  128. bool success = false;
  129. do {
  130. if(!flipper_format_read_string(ff, "type", tmp)) break;
  131. if(furi_string_equal(tmp, "raw")) {
  132. success = infrared_signal_read_raw(signal, ff);
  133. } else if(furi_string_equal(tmp, "parsed")) {
  134. success = infrared_signal_read_message(signal, ff);
  135. } else {
  136. FURI_LOG_E(TAG, "Unknown signal type");
  137. }
  138. } while(false);
  139. furi_string_free(tmp);
  140. return success;
  141. }
  142. InfraredSignal* infrared_signal_alloc() {
  143. InfraredSignal* signal = malloc(sizeof(InfraredSignal));
  144. signal->is_raw = false;
  145. signal->payload.message.protocol = InfraredProtocolUnknown;
  146. return signal;
  147. }
  148. void infrared_signal_free(InfraredSignal* signal) {
  149. infrared_signal_clear_timings(signal);
  150. free(signal);
  151. }
  152. bool infrared_signal_is_raw(InfraredSignal* signal) {
  153. return signal->is_raw;
  154. }
  155. bool infrared_signal_is_valid(InfraredSignal* signal) {
  156. return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
  157. infrared_signal_is_message_valid(&signal->payload.message);
  158. }
  159. void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
  160. if(other->is_raw) {
  161. const InfraredRawSignal* raw = &other->payload.raw;
  162. infrared_signal_set_raw_signal(
  163. signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
  164. } else {
  165. const InfraredMessage* message = &other->payload.message;
  166. infrared_signal_set_message(signal, message);
  167. }
  168. }
  169. void infrared_signal_set_raw_signal(
  170. InfraredSignal* signal,
  171. const uint32_t* timings,
  172. size_t timings_size,
  173. uint32_t frequency,
  174. float duty_cycle) {
  175. infrared_signal_clear_timings(signal);
  176. // If the frequency is out of bounds, set it to the closest bound same for duty cycle
  177. // TODO: Should we return error instead? Also infrared_signal_is_valid is used only in CLI for some reason?!
  178. if(frequency > INFRARED_MAX_FREQUENCY) {
  179. frequency = INFRARED_MAX_FREQUENCY;
  180. } else if(frequency < INFRARED_MIN_FREQUENCY) {
  181. frequency = INFRARED_MIN_FREQUENCY;
  182. }
  183. if((duty_cycle <= (float)0) || (duty_cycle > (float)1)) {
  184. duty_cycle = (float)0.33;
  185. }
  186. // In case of timings out of bounds we just call return
  187. if((timings_size <= 0) || (timings_size > MAX_TIMINGS_AMOUNT)) {
  188. return;
  189. }
  190. signal->is_raw = true;
  191. signal->payload.raw.timings_size = timings_size;
  192. signal->payload.raw.frequency = frequency;
  193. signal->payload.raw.duty_cycle = duty_cycle;
  194. signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
  195. memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
  196. }
  197. InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
  198. furi_assert(signal->is_raw);
  199. return &signal->payload.raw;
  200. }
  201. void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
  202. infrared_signal_clear_timings(signal);
  203. signal->is_raw = false;
  204. signal->payload.message = *message;
  205. }
  206. InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
  207. furi_assert(!signal->is_raw);
  208. return &signal->payload.message;
  209. }
  210. bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
  211. if(!flipper_format_write_comment_cstr(ff, "") ||
  212. !flipper_format_write_string_cstr(ff, "name", name)) {
  213. return false;
  214. } else if(signal->is_raw) {
  215. return infrared_signal_save_raw(&signal->payload.raw, ff);
  216. } else {
  217. return infrared_signal_save_message(&signal->payload.message, ff);
  218. }
  219. }
  220. bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
  221. FuriString* tmp = furi_string_alloc();
  222. bool success = false;
  223. do {
  224. if(!flipper_format_read_string(ff, "name", tmp)) break;
  225. furi_string_set(name, tmp);
  226. if(!infrared_signal_read_body(signal, ff)) break;
  227. success = true;
  228. } while(0);
  229. furi_string_free(tmp);
  230. return success;
  231. }
  232. bool infrared_signal_search_and_read(
  233. InfraredSignal* signal,
  234. FlipperFormat* ff,
  235. const FuriString* name) {
  236. bool success = false;
  237. FuriString* tmp = furi_string_alloc();
  238. do {
  239. bool is_name_found = false;
  240. while(flipper_format_read_string(ff, "name", tmp)) {
  241. is_name_found = furi_string_equal(name, tmp);
  242. if(is_name_found) break;
  243. }
  244. if(!is_name_found) break; //-V547
  245. if(!infrared_signal_read_body(signal, ff)) break; //-V779
  246. success = true;
  247. } while(false);
  248. furi_string_free(tmp);
  249. return success;
  250. }
  251. void infrared_signal_transmit(InfraredSignal* signal) {
  252. if(signal->is_raw) {
  253. InfraredRawSignal* raw_signal = &signal->payload.raw;
  254. infrared_send_raw_ext(
  255. raw_signal->timings,
  256. raw_signal->timings_size,
  257. true,
  258. raw_signal->frequency,
  259. raw_signal->duty_cycle);
  260. } else {
  261. InfraredMessage* message = &signal->payload.message;
  262. infrared_send(message, 1);
  263. }
  264. }
  265. void infrared_signal_transmit_times(InfraredSignal* signal, int times) {
  266. if(signal->is_raw) {
  267. InfraredRawSignal* raw_signal = &signal->payload.raw;
  268. infrared_send_raw_ext(
  269. raw_signal->timings,
  270. raw_signal->timings_size,
  271. true,
  272. raw_signal->frequency,
  273. raw_signal->duty_cycle);
  274. } else {
  275. InfraredMessage* message = &signal->payload.message;
  276. infrared_send(message, times);
  277. }
  278. }