infrared_signal.c 11 KB

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