infrared_worker.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. #include "infrared_worker.h"
  2. #include <furi_hal_infrared.h>
  3. #include <float_tools.h>
  4. #include <core/check.h>
  5. #include <core/common_defines.h>
  6. #include <notification/notification_messages.h>
  7. #define INFRARED_WORKER_RX_TIMEOUT INFRARED_RAW_RX_TIMING_DELAY_US
  8. #define INFRARED_WORKER_RX_RECEIVED 0x01
  9. #define INFRARED_WORKER_RX_TIMEOUT_RECEIVED 0x02
  10. #define INFRARED_WORKER_OVERRUN 0x04
  11. #define INFRARED_WORKER_EXIT 0x08
  12. #define INFRARED_WORKER_TX_FILL_BUFFER 0x10
  13. #define INFRARED_WORKER_TX_MESSAGE_SENT 0x20
  14. #define INFRARED_WORKER_ALL_RX_EVENTS \
  15. (INFRARED_WORKER_RX_RECEIVED | INFRARED_WORKER_RX_TIMEOUT_RECEIVED | \
  16. INFRARED_WORKER_OVERRUN | INFRARED_WORKER_EXIT)
  17. #define INFRARED_WORKER_ALL_TX_EVENTS \
  18. (INFRARED_WORKER_TX_FILL_BUFFER | INFRARED_WORKER_TX_MESSAGE_SENT | INFRARED_WORKER_EXIT)
  19. #define INFRARED_WORKER_ALL_EVENTS (INFRARED_WORKER_ALL_RX_EVENTS | INFRARED_WORKER_ALL_TX_EVENTS)
  20. typedef enum {
  21. InfraredWorkerStateIdle,
  22. InfraredWorkerStateRunRx,
  23. InfraredWorkerStateRunTx,
  24. InfraredWorkerStateWaitTxEnd,
  25. InfraredWorkerStateStopTx,
  26. InfraredWorkerStateStartTx,
  27. } InfraredWorkerState;
  28. struct InfraredWorkerSignal {
  29. bool decoded;
  30. size_t timings_cnt;
  31. union {
  32. InfraredMessage message;
  33. /* +1 is for pause we add at the beginning */
  34. uint32_t timings[MAX_TIMINGS_AMOUNT + 1];
  35. };
  36. };
  37. struct InfraredWorker {
  38. FuriThread* thread;
  39. FuriStreamBuffer* stream;
  40. InfraredWorkerSignal signal;
  41. InfraredWorkerState state;
  42. InfraredEncoderHandler* infrared_encoder;
  43. InfraredDecoderHandler* infrared_decoder;
  44. NotificationApp* notification;
  45. bool blink_enable;
  46. bool decode_enable;
  47. union {
  48. struct {
  49. InfraredWorkerGetSignalCallback get_signal_callback;
  50. InfraredWorkerMessageSentCallback message_sent_callback;
  51. void* get_signal_context;
  52. void* message_sent_context;
  53. uint32_t frequency;
  54. float duty_cycle;
  55. uint32_t tx_raw_cnt;
  56. bool need_reinitialization;
  57. bool steady_signal_sent;
  58. } tx;
  59. struct {
  60. InfraredWorkerReceivedSignalCallback received_signal_callback;
  61. void* received_signal_context;
  62. bool overrun;
  63. } rx;
  64. };
  65. };
  66. typedef struct {
  67. uint32_t duration;
  68. bool level;
  69. FuriHalInfraredTxGetDataState state;
  70. } InfraredWorkerTiming;
  71. static int32_t infrared_worker_tx_thread(void* context);
  72. static FuriHalInfraredTxGetDataState
  73. infrared_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level);
  74. static void infrared_worker_furi_hal_message_sent_isr_callback(void* context);
  75. static void infrared_worker_rx_timeout_callback(void* context) {
  76. InfraredWorker* instance = context;
  77. uint32_t flags_set = furi_thread_flags_set(
  78. furi_thread_get_id(instance->thread), INFRARED_WORKER_RX_TIMEOUT_RECEIVED);
  79. furi_check(flags_set & INFRARED_WORKER_RX_TIMEOUT_RECEIVED);
  80. }
  81. static void infrared_worker_rx_callback(void* context, bool level, uint32_t duration) {
  82. InfraredWorker* instance = context;
  83. furi_assert(duration != 0);
  84. LevelDuration level_duration = level_duration_make(level, duration);
  85. size_t ret =
  86. furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);
  87. uint32_t events = (ret == sizeof(LevelDuration)) ? INFRARED_WORKER_RX_RECEIVED :
  88. INFRARED_WORKER_OVERRUN;
  89. uint32_t flags_set = furi_thread_flags_set(furi_thread_get_id(instance->thread), events);
  90. furi_check(flags_set & events);
  91. }
  92. static void infrared_worker_process_timeout(InfraredWorker* instance) {
  93. if(instance->signal.timings_cnt < 2) return;
  94. const InfraredMessage* message_decoded =
  95. infrared_check_decoder_ready(instance->infrared_decoder);
  96. if(message_decoded) {
  97. instance->signal.message = *message_decoded;
  98. instance->signal.timings_cnt = 0;
  99. instance->signal.decoded = true;
  100. } else {
  101. instance->signal.decoded = false;
  102. }
  103. if(instance->rx.received_signal_callback)
  104. instance->rx.received_signal_callback(
  105. instance->rx.received_signal_context, &instance->signal);
  106. }
  107. static void
  108. infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) {
  109. const InfraredMessage* message_decoded =
  110. instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) :
  111. NULL;
  112. if(message_decoded) {
  113. instance->signal.message = *message_decoded;
  114. instance->signal.timings_cnt = 0;
  115. instance->signal.decoded = true;
  116. if(instance->rx.received_signal_callback)
  117. instance->rx.received_signal_callback(
  118. instance->rx.received_signal_context, &instance->signal);
  119. } else {
  120. /* Skip first timing if it starts from Space */
  121. if((instance->signal.timings_cnt == 0) && !level) {
  122. return;
  123. }
  124. if(instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) {
  125. instance->signal.timings[instance->signal.timings_cnt] = duration;
  126. ++instance->signal.timings_cnt;
  127. } else {
  128. uint32_t flags_set = furi_thread_flags_set(
  129. furi_thread_get_id(instance->thread), INFRARED_WORKER_OVERRUN);
  130. furi_check(flags_set & INFRARED_WORKER_OVERRUN);
  131. instance->rx.overrun = true;
  132. }
  133. }
  134. }
  135. static int32_t infrared_worker_rx_thread(void* thread_context) {
  136. InfraredWorker* instance = thread_context;
  137. uint32_t events = 0;
  138. LevelDuration level_duration;
  139. TickType_t last_blink_time = 0;
  140. while(1) {
  141. events = furi_thread_flags_wait(INFRARED_WORKER_ALL_RX_EVENTS, 0, FuriWaitForever);
  142. furi_check(events & INFRARED_WORKER_ALL_RX_EVENTS); /* at least one caught */
  143. if(events & INFRARED_WORKER_RX_RECEIVED) {
  144. if(!instance->rx.overrun && instance->blink_enable &&
  145. ((xTaskGetTickCount() - last_blink_time) > 80)) {
  146. last_blink_time = xTaskGetTickCount();
  147. notification_message(instance->notification, &sequence_blink_blue_10);
  148. }
  149. if(instance->signal.timings_cnt == 0)
  150. notification_message(instance->notification, &sequence_display_backlight_on);
  151. while(sizeof(LevelDuration) ==
  152. furi_stream_buffer_receive(
  153. instance->stream, &level_duration, sizeof(LevelDuration), 0)) {
  154. if(!instance->rx.overrun) {
  155. bool level = level_duration_get_level(level_duration);
  156. uint32_t duration = level_duration_get_duration(level_duration);
  157. infrared_worker_process_timings(instance, duration, level);
  158. }
  159. }
  160. }
  161. if(events & INFRARED_WORKER_OVERRUN) {
  162. printf("#");
  163. infrared_reset_decoder(instance->infrared_decoder);
  164. instance->signal.timings_cnt = 0;
  165. if(instance->blink_enable)
  166. notification_message(instance->notification, &sequence_set_red_255);
  167. }
  168. if(events & INFRARED_WORKER_RX_TIMEOUT_RECEIVED) {
  169. if(instance->rx.overrun) {
  170. printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT);
  171. instance->rx.overrun = false;
  172. if(instance->blink_enable)
  173. notification_message(instance->notification, &sequence_reset_red);
  174. } else {
  175. infrared_worker_process_timeout(instance);
  176. }
  177. instance->signal.timings_cnt = 0;
  178. }
  179. if(events & INFRARED_WORKER_EXIT) break;
  180. }
  181. return 0;
  182. }
  183. void infrared_worker_rx_set_received_signal_callback(
  184. InfraredWorker* instance,
  185. InfraredWorkerReceivedSignalCallback callback,
  186. void* context) {
  187. furi_assert(instance);
  188. instance->rx.received_signal_callback = callback;
  189. instance->rx.received_signal_context = context;
  190. }
  191. InfraredWorker* infrared_worker_alloc() {
  192. InfraredWorker* instance = malloc(sizeof(InfraredWorker));
  193. instance->thread = furi_thread_alloc_ex("InfraredWorker", 2048, NULL, instance);
  194. size_t buffer_size =
  195. MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1),
  196. sizeof(LevelDuration) * MAX_TIMINGS_AMOUNT);
  197. instance->stream = furi_stream_buffer_alloc(buffer_size, sizeof(InfraredWorkerTiming));
  198. instance->infrared_decoder = infrared_alloc_decoder();
  199. instance->infrared_encoder = infrared_alloc_encoder();
  200. instance->blink_enable = false;
  201. instance->decode_enable = true;
  202. instance->notification = furi_record_open(RECORD_NOTIFICATION);
  203. instance->state = InfraredWorkerStateIdle;
  204. return instance;
  205. }
  206. void infrared_worker_free(InfraredWorker* instance) {
  207. furi_assert(instance);
  208. furi_assert(instance->state == InfraredWorkerStateIdle);
  209. furi_record_close(RECORD_NOTIFICATION);
  210. infrared_free_decoder(instance->infrared_decoder);
  211. infrared_free_encoder(instance->infrared_encoder);
  212. furi_stream_buffer_free(instance->stream);
  213. furi_thread_free(instance->thread);
  214. free(instance);
  215. }
  216. void infrared_worker_rx_start(InfraredWorker* instance) {
  217. furi_assert(instance);
  218. furi_assert(instance->state == InfraredWorkerStateIdle);
  219. furi_stream_set_trigger_level(instance->stream, sizeof(LevelDuration));
  220. furi_thread_set_callback(instance->thread, infrared_worker_rx_thread);
  221. furi_thread_start(instance->thread);
  222. furi_hal_infrared_async_rx_set_capture_isr_callback(infrared_worker_rx_callback, instance);
  223. furi_hal_infrared_async_rx_set_timeout_isr_callback(
  224. infrared_worker_rx_timeout_callback, instance);
  225. furi_hal_infrared_async_rx_start();
  226. furi_hal_infrared_async_rx_set_timeout(INFRARED_WORKER_RX_TIMEOUT);
  227. instance->rx.overrun = false;
  228. instance->state = InfraredWorkerStateRunRx;
  229. }
  230. void infrared_worker_rx_stop(InfraredWorker* instance) {
  231. furi_assert(instance);
  232. furi_assert(instance->state == InfraredWorkerStateRunRx);
  233. furi_hal_infrared_async_rx_set_timeout_isr_callback(NULL, NULL);
  234. furi_hal_infrared_async_rx_set_capture_isr_callback(NULL, NULL);
  235. furi_hal_infrared_async_rx_stop();
  236. furi_thread_flags_set(furi_thread_get_id(instance->thread), INFRARED_WORKER_EXIT);
  237. furi_thread_join(instance->thread);
  238. FuriStatus status = furi_stream_buffer_reset(instance->stream);
  239. furi_assert(status == FuriStatusOk);
  240. (void)status;
  241. instance->state = InfraredWorkerStateIdle;
  242. }
  243. bool infrared_worker_signal_is_decoded(const InfraredWorkerSignal* signal) {
  244. furi_assert(signal);
  245. return signal->decoded;
  246. }
  247. void infrared_worker_get_raw_signal(
  248. const InfraredWorkerSignal* signal,
  249. const uint32_t** timings,
  250. size_t* timings_cnt) {
  251. furi_assert(signal);
  252. furi_assert(timings);
  253. furi_assert(timings_cnt);
  254. *timings = signal->timings;
  255. *timings_cnt = signal->timings_cnt;
  256. }
  257. const InfraredMessage* infrared_worker_get_decoded_signal(const InfraredWorkerSignal* signal) {
  258. furi_assert(signal);
  259. return &signal->message;
  260. }
  261. void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable) {
  262. furi_assert(instance);
  263. instance->blink_enable = enable;
  264. }
  265. void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) {
  266. furi_assert(instance);
  267. instance->decode_enable = enable;
  268. }
  269. void infrared_worker_tx_start(InfraredWorker* instance) {
  270. furi_assert(instance);
  271. furi_assert(instance->state == InfraredWorkerStateIdle);
  272. furi_assert(instance->tx.get_signal_callback);
  273. // size have to be greater than api hal infrared async tx buffer size
  274. furi_stream_set_trigger_level(instance->stream, sizeof(InfraredWorkerTiming));
  275. furi_thread_set_callback(instance->thread, infrared_worker_tx_thread);
  276. instance->tx.steady_signal_sent = false;
  277. instance->tx.need_reinitialization = false;
  278. furi_hal_infrared_async_tx_set_data_isr_callback(
  279. infrared_worker_furi_hal_data_isr_callback, instance);
  280. furi_hal_infrared_async_tx_set_signal_sent_isr_callback(
  281. infrared_worker_furi_hal_message_sent_isr_callback, instance);
  282. instance->state = InfraredWorkerStateStartTx;
  283. furi_thread_start(instance->thread);
  284. }
  285. static void infrared_worker_furi_hal_message_sent_isr_callback(void* context) {
  286. InfraredWorker* instance = context;
  287. uint32_t flags_set = furi_thread_flags_set(
  288. furi_thread_get_id(instance->thread), INFRARED_WORKER_TX_MESSAGE_SENT);
  289. furi_check(flags_set & INFRARED_WORKER_TX_MESSAGE_SENT);
  290. }
  291. static FuriHalInfraredTxGetDataState
  292. infrared_worker_furi_hal_data_isr_callback(void* context, uint32_t* duration, bool* level) {
  293. furi_assert(context);
  294. furi_assert(duration);
  295. furi_assert(level);
  296. InfraredWorker* instance = context;
  297. InfraredWorkerTiming timing;
  298. FuriHalInfraredTxGetDataState state;
  299. if(sizeof(InfraredWorkerTiming) ==
  300. furi_stream_buffer_receive(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0)) {
  301. *level = timing.level;
  302. *duration = timing.duration;
  303. state = timing.state;
  304. } else {
  305. furi_assert(0);
  306. *level = 0;
  307. *duration = 100;
  308. state = FuriHalInfraredTxGetDataStateDone;
  309. }
  310. uint32_t flags_set = furi_thread_flags_set(
  311. furi_thread_get_id(instance->thread), INFRARED_WORKER_TX_FILL_BUFFER);
  312. furi_check(flags_set & INFRARED_WORKER_TX_FILL_BUFFER);
  313. return state;
  314. }
  315. static bool infrared_get_new_signal(InfraredWorker* instance) {
  316. bool new_signal_obtained = false;
  317. InfraredWorkerGetSignalResponse response =
  318. instance->tx.get_signal_callback(instance->tx.get_signal_context, instance);
  319. if(response == InfraredWorkerGetSignalResponseNew) {
  320. uint32_t new_tx_frequency = 0;
  321. float new_tx_duty_cycle = 0;
  322. if(instance->signal.decoded) {
  323. new_tx_frequency = infrared_get_protocol_frequency(instance->signal.message.protocol);
  324. new_tx_duty_cycle =
  325. infrared_get_protocol_duty_cycle(instance->signal.message.protocol);
  326. } else {
  327. furi_assert(instance->signal.timings_cnt > 1);
  328. new_tx_frequency = INFRARED_COMMON_CARRIER_FREQUENCY;
  329. new_tx_duty_cycle = INFRARED_COMMON_DUTY_CYCLE;
  330. }
  331. instance->tx.tx_raw_cnt = 0;
  332. instance->tx.need_reinitialization =
  333. (new_tx_frequency != instance->tx.frequency) ||
  334. !float_is_equal(new_tx_duty_cycle, instance->tx.duty_cycle);
  335. instance->tx.frequency = new_tx_frequency;
  336. instance->tx.duty_cycle = new_tx_duty_cycle;
  337. if(instance->signal.decoded) {
  338. infrared_reset_encoder(instance->infrared_encoder, &instance->signal.message);
  339. }
  340. new_signal_obtained = true;
  341. } else if(response == InfraredWorkerGetSignalResponseSame) {
  342. new_signal_obtained = true;
  343. /* no need to reinit */
  344. } else if(response == InfraredWorkerGetSignalResponseStop) {
  345. new_signal_obtained = false;
  346. } else {
  347. furi_assert(0);
  348. }
  349. return new_signal_obtained;
  350. }
  351. static bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) {
  352. bool new_data_available = true;
  353. InfraredWorkerTiming timing;
  354. InfraredStatus status = InfraredStatusError;
  355. while(!furi_stream_buffer_is_full(instance->stream) && !instance->tx.need_reinitialization &&
  356. new_data_available) {
  357. if(instance->signal.decoded) {
  358. status = infrared_encode(instance->infrared_encoder, &timing.duration, &timing.level);
  359. } else {
  360. timing.duration = instance->signal.timings[instance->tx.tx_raw_cnt];
  361. /* raw always starts from Mark, but we fill it with space delay at start */
  362. timing.level = (instance->tx.tx_raw_cnt % 2);
  363. ++instance->tx.tx_raw_cnt;
  364. if(instance->tx.tx_raw_cnt >= instance->signal.timings_cnt) {
  365. instance->tx.tx_raw_cnt = 0;
  366. status = InfraredStatusDone;
  367. } else {
  368. status = InfraredStatusOk;
  369. }
  370. }
  371. if(status == InfraredStatusError) {
  372. furi_assert(0);
  373. new_data_available = false;
  374. break;
  375. } else if(status == InfraredStatusOk) {
  376. timing.state = FuriHalInfraredTxGetDataStateOk;
  377. } else if(status == InfraredStatusDone) {
  378. timing.state = FuriHalInfraredTxGetDataStateDone;
  379. new_data_available = infrared_get_new_signal(instance);
  380. if(instance->tx.need_reinitialization || !new_data_available) {
  381. timing.state = FuriHalInfraredTxGetDataStateLastDone;
  382. }
  383. } else {
  384. furi_assert(0);
  385. }
  386. uint32_t written_size =
  387. furi_stream_buffer_send(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0);
  388. furi_assert(sizeof(InfraredWorkerTiming) == written_size);
  389. (void)written_size;
  390. }
  391. return new_data_available;
  392. }
  393. static int32_t infrared_worker_tx_thread(void* thread_context) {
  394. InfraredWorker* instance = thread_context;
  395. furi_assert(instance->state == InfraredWorkerStateStartTx);
  396. furi_assert(thread_context);
  397. size_t repeats_left =
  398. instance->signal.decoded ?
  399. infrared_get_protocol_min_repeat_count(instance->signal.message.protocol) :
  400. 1;
  401. uint32_t events = 0;
  402. bool exit_pending = false;
  403. bool running = infrared_get_new_signal(instance);
  404. furi_assert(running);
  405. while(running) {
  406. switch(instance->state) {
  407. case InfraredWorkerStateStartTx:
  408. --repeats_left; /* The first message does not result in TX_MESSAGE_SENT event for some reason */
  409. instance->tx.need_reinitialization = false;
  410. const bool new_data_available = infrared_worker_tx_fill_buffer(instance);
  411. furi_hal_infrared_async_tx_start(instance->tx.frequency, instance->tx.duty_cycle);
  412. if(!new_data_available) {
  413. instance->state = InfraredWorkerStateStopTx;
  414. } else if(instance->tx.need_reinitialization) {
  415. instance->state = InfraredWorkerStateWaitTxEnd;
  416. } else {
  417. instance->state = InfraredWorkerStateRunTx;
  418. }
  419. break;
  420. case InfraredWorkerStateStopTx:
  421. furi_hal_infrared_async_tx_stop();
  422. running = false;
  423. break;
  424. case InfraredWorkerStateWaitTxEnd:
  425. furi_hal_infrared_async_tx_wait_termination();
  426. instance->state = InfraredWorkerStateStartTx;
  427. events = furi_thread_flags_get();
  428. if(events & INFRARED_WORKER_EXIT) {
  429. running = false;
  430. break;
  431. }
  432. break;
  433. case InfraredWorkerStateRunTx:
  434. events = furi_thread_flags_wait(
  435. INFRARED_WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
  436. furi_check(events & INFRARED_WORKER_ALL_TX_EVENTS); /* at least one caught */
  437. if(events & INFRARED_WORKER_EXIT) {
  438. exit_pending = true;
  439. }
  440. if(events & INFRARED_WORKER_TX_FILL_BUFFER) {
  441. infrared_worker_tx_fill_buffer(instance);
  442. if(instance->tx.need_reinitialization) {
  443. instance->state = InfraredWorkerStateWaitTxEnd;
  444. }
  445. }
  446. if(events & INFRARED_WORKER_TX_MESSAGE_SENT) {
  447. if(repeats_left > 0) {
  448. --repeats_left;
  449. }
  450. if(instance->tx.message_sent_callback) {
  451. instance->tx.message_sent_callback(instance->tx.message_sent_context);
  452. }
  453. }
  454. if(exit_pending && repeats_left == 0) {
  455. instance->state = InfraredWorkerStateStopTx;
  456. }
  457. break;
  458. default:
  459. furi_assert(0);
  460. break;
  461. }
  462. }
  463. return 0;
  464. }
  465. void infrared_worker_tx_set_get_signal_callback(
  466. InfraredWorker* instance,
  467. InfraredWorkerGetSignalCallback callback,
  468. void* context) {
  469. furi_assert(instance);
  470. instance->tx.get_signal_callback = callback;
  471. instance->tx.get_signal_context = context;
  472. }
  473. void infrared_worker_tx_set_signal_sent_callback(
  474. InfraredWorker* instance,
  475. InfraredWorkerMessageSentCallback callback,
  476. void* context) {
  477. furi_assert(instance);
  478. instance->tx.message_sent_callback = callback;
  479. instance->tx.message_sent_context = context;
  480. }
  481. void infrared_worker_tx_stop(InfraredWorker* instance) {
  482. furi_assert(instance);
  483. furi_assert(instance->state != InfraredWorkerStateRunRx);
  484. furi_thread_flags_set(furi_thread_get_id(instance->thread), INFRARED_WORKER_EXIT);
  485. furi_thread_join(instance->thread);
  486. furi_hal_infrared_async_tx_set_data_isr_callback(NULL, NULL);
  487. furi_hal_infrared_async_tx_set_signal_sent_isr_callback(NULL, NULL);
  488. instance->signal.timings_cnt = 0;
  489. FuriStatus status = furi_stream_buffer_reset(instance->stream);
  490. furi_assert(status == FuriStatusOk);
  491. (void)status;
  492. instance->state = InfraredWorkerStateIdle;
  493. }
  494. void infrared_worker_set_decoded_signal(InfraredWorker* instance, const InfraredMessage* message) {
  495. furi_assert(instance);
  496. furi_assert(message);
  497. instance->signal.decoded = true;
  498. instance->signal.message = *message;
  499. }
  500. void infrared_worker_set_raw_signal(
  501. InfraredWorker* instance,
  502. const uint32_t* timings,
  503. size_t timings_cnt) {
  504. furi_assert(instance);
  505. furi_assert(timings);
  506. furi_assert(timings_cnt > 0);
  507. size_t max_copy_num = COUNT_OF(instance->signal.timings) - 1;
  508. furi_check(timings_cnt <= max_copy_num);
  509. instance->signal.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US;
  510. memcpy(&instance->signal.timings[1], timings, timings_cnt * sizeof(uint32_t));
  511. instance->signal.decoded = false;
  512. instance->signal.timings_cnt = timings_cnt + 1;
  513. }
  514. InfraredWorkerGetSignalResponse
  515. infrared_worker_tx_get_signal_steady_callback(void* context, InfraredWorker* instance) {
  516. UNUSED(context);
  517. InfraredWorkerGetSignalResponse response = instance->tx.steady_signal_sent ?
  518. InfraredWorkerGetSignalResponseSame :
  519. InfraredWorkerGetSignalResponseNew;
  520. instance->tx.steady_signal_sent = true;
  521. return response;
  522. }