irda_worker.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #include "irda_worker.h"
  2. #include <irda.h>
  3. #include <api-hal-irda.h>
  4. #include <limits.h>
  5. #include <stdint.h>
  6. #include <stream_buffer.h>
  7. #include <furi.h>
  8. #include <notification/notification-messages.h>
  9. #define MAX_TIMINGS_AMOUNT 500
  10. #define IRDA_WORKER_RX_TIMEOUT 150 // ms
  11. #define IRDA_WORKER_RX_RECEIVED 0x01
  12. #define IRDA_WORKER_RX_TIMEOUT_RECEIVED 0x02
  13. #define IRDA_WORKER_OVERRUN 0x04
  14. #define IRDA_WORKER_EXIT 0x08
  15. struct IrdaWorkerSignal {
  16. bool decoded;
  17. size_t timings_cnt;
  18. union {
  19. IrdaMessage message;
  20. uint32_t timings[MAX_TIMINGS_AMOUNT];
  21. } data;
  22. };
  23. struct IrdaWorker {
  24. FuriThread* thread;
  25. IrdaDecoderHandler* irda_decoder;
  26. StreamBufferHandle_t stream;
  27. TaskHandle_t worker_handle;
  28. IrdaWorkerSignal signal;
  29. IrdaWorkerReceivedSignalCallback received_signal_callback;
  30. void* context;
  31. bool blink_enable;
  32. bool overrun;
  33. NotificationApp* notification;
  34. };
  35. static void irda_worker_rx_timeout_callback(void* context) {
  36. IrdaWorker* instance = context;
  37. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  38. xTaskNotifyFromISR(instance->worker_handle, IRDA_WORKER_RX_TIMEOUT_RECEIVED, eSetBits, &xHigherPriorityTaskWoken);
  39. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  40. }
  41. static void irda_worker_rx_callback(void* context, bool level, uint32_t duration) {
  42. IrdaWorker* instance = context;
  43. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  44. LevelDuration level_duration = level_duration_make(level, duration);
  45. size_t ret =
  46. xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken);
  47. uint32_t notify_value = (ret == sizeof(LevelDuration)) ? IRDA_WORKER_RX_RECEIVED : IRDA_WORKER_OVERRUN;
  48. xTaskNotifyFromISR(instance->worker_handle, notify_value, eSetBits, &xHigherPriorityTaskWoken);
  49. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  50. }
  51. static void irda_worker_process_timeout(IrdaWorker* instance) {
  52. if (instance->signal.timings_cnt < 2)
  53. return;
  54. instance->signal.decoded = false;
  55. if (instance->received_signal_callback)
  56. instance->received_signal_callback(instance->context, &instance->signal);
  57. }
  58. static void irda_worker_process_timings(IrdaWorker* instance, uint32_t duration, bool level) {
  59. const IrdaMessage* message_decoded = irda_decode(instance->irda_decoder, level, duration);
  60. if (message_decoded) {
  61. instance->signal.data.message = *message_decoded;
  62. instance->signal.timings_cnt = 0;
  63. instance->signal.decoded = true;
  64. if (instance->received_signal_callback)
  65. instance->received_signal_callback(instance->context, &instance->signal);
  66. } else {
  67. /* Skip first timing if it's starts from Space */
  68. if ((instance->signal.timings_cnt == 0) && !level) {
  69. return;
  70. }
  71. if (instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) {
  72. instance->signal.data.timings[instance->signal.timings_cnt] = duration;
  73. ++instance->signal.timings_cnt;
  74. } else {
  75. xTaskNotify(instance->worker_handle, IRDA_WORKER_OVERRUN, eSetBits);
  76. instance->overrun = true;
  77. }
  78. }
  79. }
  80. static int32_t irda_worker_thread_callback(void* context) {
  81. IrdaWorker* instance = context;
  82. uint32_t notify_value = 0;
  83. LevelDuration level_duration;
  84. TickType_t last_blink_time = 0;
  85. while(1) {
  86. BaseType_t result;
  87. result = xTaskNotifyWait(pdFALSE, ULONG_MAX, &notify_value, 1000);
  88. if (result != pdPASS)
  89. continue;
  90. if (notify_value & IRDA_WORKER_RX_RECEIVED) {
  91. if (!instance->overrun && instance->blink_enable && ((xTaskGetTickCount() - last_blink_time) > 80)) {
  92. last_blink_time = xTaskGetTickCount();
  93. notification_message(instance->notification, &sequence_blink_blue_10);
  94. }
  95. if (instance->signal.timings_cnt == 0)
  96. notification_message(instance->notification, &sequence_display_on);
  97. while (sizeof(LevelDuration) == xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 0)) {
  98. if (!instance->overrun) {
  99. bool level = level_duration_get_level(level_duration);
  100. uint32_t duration = level_duration_get_duration(level_duration);
  101. irda_worker_process_timings(instance, duration, level);
  102. }
  103. }
  104. }
  105. if (notify_value & IRDA_WORKER_OVERRUN) {
  106. printf("#");
  107. irda_reset_decoder(instance->irda_decoder);
  108. instance->signal.timings_cnt = 0;
  109. if (instance->blink_enable)
  110. notification_message(instance->notification, &sequence_set_red_255);
  111. }
  112. if (notify_value & IRDA_WORKER_RX_TIMEOUT_RECEIVED) {
  113. if (instance->overrun) {
  114. printf("\nOVERRUN, max samples: %d\n", MAX_TIMINGS_AMOUNT);
  115. instance->overrun = false;
  116. if (instance->blink_enable)
  117. notification_message(instance->notification, &sequence_reset_red);
  118. } else {
  119. irda_worker_process_timeout(instance);
  120. }
  121. instance->signal.timings_cnt = 0;
  122. }
  123. if (notify_value & IRDA_WORKER_EXIT)
  124. break;
  125. }
  126. return 0;
  127. }
  128. void irda_worker_set_received_signal_callback(IrdaWorker* instance, IrdaWorkerReceivedSignalCallback callback) {
  129. furi_assert(instance);
  130. instance->received_signal_callback = callback;
  131. }
  132. IrdaWorker* irda_worker_alloc() {
  133. IrdaWorker* instance = furi_alloc(sizeof(IrdaWorker));
  134. instance->thread = furi_thread_alloc();
  135. furi_thread_set_name(instance->thread, "irda_worker");
  136. furi_thread_set_stack_size(instance->thread, 2048);
  137. furi_thread_set_context(instance->thread, instance);
  138. furi_thread_set_callback(instance->thread, irda_worker_thread_callback);
  139. instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 512, sizeof(LevelDuration));
  140. instance->irda_decoder = irda_alloc_decoder();
  141. instance->blink_enable = false;
  142. instance->notification = furi_record_open("notification");
  143. return instance;
  144. }
  145. void irda_worker_free(IrdaWorker* instance) {
  146. furi_assert(instance);
  147. furi_assert(!instance->worker_handle);
  148. furi_record_close("notification");
  149. irda_free_decoder(instance->irda_decoder);
  150. vStreamBufferDelete(instance->stream);
  151. furi_thread_free(instance->thread);
  152. free(instance);
  153. }
  154. void irda_worker_set_context(IrdaWorker* instance, void* context) {
  155. furi_assert(instance);
  156. instance->context = context;
  157. }
  158. void irda_worker_start(IrdaWorker* instance) {
  159. furi_assert(instance);
  160. furi_assert(!instance->worker_handle);
  161. furi_thread_start(instance->thread);
  162. instance->worker_handle = furi_thread_get_thread_id(instance->thread);
  163. api_hal_irda_rx_irq_init();
  164. api_hal_irda_rx_timeout_irq_init(IRDA_WORKER_RX_TIMEOUT);
  165. api_hal_irda_rx_irq_set_callback(irda_worker_rx_callback, instance);
  166. api_hal_irda_rx_timeout_irq_set_callback(irda_worker_rx_timeout_callback, instance);
  167. }
  168. void irda_worker_stop(IrdaWorker* instance) {
  169. furi_assert(instance);
  170. furi_assert(instance->worker_handle);
  171. api_hal_irda_rx_timeout_irq_set_callback(NULL, NULL);
  172. api_hal_irda_rx_irq_set_callback(NULL, NULL);
  173. api_hal_irda_rx_irq_deinit();
  174. xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits);
  175. instance->worker_handle = NULL;
  176. furi_thread_join(instance->thread);
  177. }
  178. bool irda_worker_signal_is_decoded(const IrdaWorkerSignal* signal) {
  179. furi_assert(signal);
  180. return signal->decoded;
  181. }
  182. void irda_worker_get_raw_signal(const IrdaWorkerSignal* signal, const uint32_t** timings, size_t* timings_cnt) {
  183. furi_assert(signal);
  184. furi_assert(timings);
  185. furi_assert(timings_cnt);
  186. *timings = signal->data.timings;
  187. *timings_cnt = signal->timings_cnt;
  188. }
  189. const IrdaMessage* irda_worker_get_decoded_message(const IrdaWorkerSignal* signal) {
  190. furi_assert(signal);
  191. return &signal->data.message;
  192. }
  193. void irda_worker_enable_blink_on_receiving(IrdaWorker* instance, bool enable) {
  194. furi_assert(instance);
  195. instance->blink_enable = enable;
  196. }