infrared_controller.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #include "infrared_controller.h"
  2. #include <furi.h>
  3. #include <infrared_worker.h>
  4. #include <infrared_signal.h>
  5. #include <notification/notification_messages.h>
  6. #include <furi_hal_gpio.h>
  7. #include <furi_hal_power.h>
  8. #include <furi_hal_infrared.h>
  9. #define TAG "InfraredController"
  10. const NotificationSequence sequence_hit = {
  11. &message_vibro_on,
  12. &message_note_d4,
  13. &message_delay_1000,
  14. &message_vibro_off,
  15. &message_sound_off,
  16. NULL,
  17. };
  18. const NotificationSequence sequence_bloop = {
  19. &message_note_g3,
  20. &message_delay_50,
  21. &message_sound_off,
  22. NULL,
  23. };
  24. extern const NotificationSequence sequence_short_beep;
  25. static bool external_board_connected = false;
  26. static void infrared_setup_external_board(bool enable) {
  27. if(enable) {
  28. furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  29. furi_hal_power_enable_otg();
  30. furi_hal_infrared_set_tx_output(FuriHalInfraredTxPinExtPA7);
  31. } else {
  32. furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  33. furi_hal_power_disable_otg();
  34. furi_hal_infrared_set_tx_output(FuriHalInfraredTxPinInternal);
  35. }
  36. }
  37. void update_infrared_board_status(InfraredController* controller) {
  38. if(!controller || !controller->notification) return;
  39. FuriHalInfraredTxPin detected_pin = furi_hal_infrared_detect_tx_output();
  40. if(detected_pin == FuriHalInfraredTxPinExtPA7 && !external_board_connected) {
  41. external_board_connected = true;
  42. infrared_setup_external_board(true);
  43. notification_message(controller->notification, &sequence_short_beep);
  44. FURI_LOG_I(TAG, "External infrared board connected and powered.");
  45. } else if(detected_pin == FuriHalInfraredTxPinInternal && external_board_connected) {
  46. external_board_connected = false;
  47. infrared_setup_external_board(false);
  48. notification_message(controller->notification, &sequence_bloop);
  49. FURI_LOG_I(TAG, "External infrared board disconnected and power disabled.");
  50. }
  51. }
  52. static void infrared_reset(void* context, uint32_t arg) {
  53. UNUSED(arg);
  54. InfraredController* controller = (InfraredController*)context;
  55. // furi_stream_buffer_reset(instance->stream) not exposed to the API.
  56. // infrared_worker_rx_stop calls it internally.
  57. infrared_worker_rx_stop(controller->worker);
  58. infrared_worker_rx_start(controller->worker);
  59. controller->processing_signal = false;
  60. }
  61. static void infrared_rx_callback(void* context, InfraredWorkerSignal* received_signal) {
  62. FURI_LOG_I(TAG, "RX callback triggered");
  63. InfraredController* controller = (InfraredController*)context;
  64. if(controller->processing_signal) {
  65. FURI_LOG_W(TAG, "Already processing a signal, skipping callback");
  66. return;
  67. }
  68. controller->processing_signal = true;
  69. if(!received_signal) {
  70. FURI_LOG_E(TAG, "Received signal is NULL");
  71. controller->processing_signal = false;
  72. return;
  73. }
  74. const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
  75. FURI_LOG_I(TAG, "Received signal - signal address: %p", (void*)received_signal);
  76. if(message) {
  77. FURI_LOG_I(
  78. TAG,
  79. "Received message: protocol=%d, address=0x%lx, command=0x%lx",
  80. message->protocol,
  81. (unsigned long)message->address,
  82. (unsigned long)message->command);
  83. if((controller->team == TeamRed && message->command == IR_COMMAND_BLUE_TEAM) ||
  84. (controller->team == TeamBlue && message->command == IR_COMMAND_RED_TEAM)) {
  85. controller->hit_received = true;
  86. FURI_LOG_I(
  87. TAG, "Hit detected for team: %s", controller->team == TeamRed ? "Red" : "Blue");
  88. notification_message_block(controller->notification, &sequence_hit);
  89. }
  90. } else {
  91. FURI_LOG_W(TAG, "RX callback received NULL message");
  92. }
  93. FURI_LOG_I(TAG, "RX callback completed");
  94. furi_timer_pending_callback(infrared_reset, controller, 0);
  95. }
  96. InfraredController* infrared_controller_alloc() {
  97. FURI_LOG_I(TAG, "Allocating InfraredController");
  98. InfraredController* controller = malloc(sizeof(InfraredController));
  99. if(!controller) {
  100. FURI_LOG_E(TAG, "Failed to allocate InfraredController");
  101. return NULL;
  102. }
  103. controller->team = TeamRed;
  104. controller->worker = infrared_worker_alloc();
  105. controller->signal = infrared_signal_alloc();
  106. controller->notification = furi_record_open(RECORD_NOTIFICATION);
  107. controller->hit_received = false;
  108. controller->processing_signal = false;
  109. if(controller->worker && controller->signal && controller->notification) {
  110. FURI_LOG_I(
  111. TAG, "InfraredWorker, InfraredSignal, and NotificationApp allocated successfully");
  112. } else {
  113. FURI_LOG_E(TAG, "Failed to allocate resources");
  114. free(controller);
  115. return NULL;
  116. }
  117. FURI_LOG_I(TAG, "Setting up RX callback");
  118. infrared_worker_rx_set_received_signal_callback(
  119. controller->worker, infrared_rx_callback, controller);
  120. FURI_LOG_I(TAG, "InfraredController allocated successfully");
  121. return controller;
  122. }
  123. void infrared_controller_free(InfraredController* controller) {
  124. FURI_LOG_I(TAG, "Freeing InfraredController");
  125. if(controller) {
  126. if(controller->worker_rx_active) {
  127. FURI_LOG_I(TAG, "Stopping RX worker");
  128. infrared_worker_rx_stop(controller->worker);
  129. }
  130. FURI_LOG_I(TAG, "Freeing InfraredWorker and InfraredSignal");
  131. infrared_worker_free(controller->worker);
  132. infrared_signal_free(controller->signal);
  133. FURI_LOG_I(TAG, "Closing NotificationApp");
  134. furi_record_close(RECORD_NOTIFICATION);
  135. free(controller);
  136. FURI_LOG_I(TAG, "InfraredController freed successfully");
  137. } else {
  138. FURI_LOG_W(TAG, "Attempted to free NULL InfraredController");
  139. }
  140. }
  141. void infrared_controller_set_team(InfraredController* controller, LaserTagTeam team) {
  142. FURI_LOG_I(TAG, "Setting team to %s", team == TeamRed ? "Red" : "Blue");
  143. controller->team = team;
  144. }
  145. void infrared_controller_send(InfraredController* controller) {
  146. FURI_LOG_I(TAG, "Preparing to send infrared signal");
  147. InfraredMessage message = {
  148. .protocol = InfraredProtocolNEC,
  149. .address = 0x42,
  150. .command = (controller->team == TeamRed) ? IR_COMMAND_RED_TEAM : IR_COMMAND_BLUE_TEAM};
  151. FURI_LOG_I(
  152. TAG,
  153. "Prepared message: protocol=%d, address=0x%lx, command=0x%lx",
  154. message.protocol,
  155. (unsigned long)message.address,
  156. (unsigned long)message.command);
  157. if(controller->worker_rx_active) {
  158. FURI_LOG_I(TAG, "Stopping RX worker");
  159. infrared_worker_rx_stop(controller->worker);
  160. controller->worker_rx_active = false;
  161. }
  162. FURI_LOG_I(TAG, "Setting message for infrared signal");
  163. infrared_signal_set_message(controller->signal, &message);
  164. FURI_LOG_I(TAG, "Starting infrared signal transmission");
  165. infrared_signal_transmit(controller->signal);
  166. if(!controller->worker_rx_active) {
  167. infrared_worker_rx_start(controller->worker);
  168. controller->worker_rx_active = true;
  169. }
  170. FURI_LOG_I(TAG, "Infrared signal transmission completed");
  171. }
  172. bool infrared_controller_receive(InfraredController* controller) {
  173. FURI_LOG_I(TAG, "Starting infrared signal reception");
  174. if(controller->processing_signal) {
  175. FURI_LOG_W(TAG, "Cannot start reception, another signal is still being processed");
  176. return false;
  177. }
  178. if(!controller->worker_rx_active) {
  179. infrared_worker_rx_start(controller->worker);
  180. controller->worker_rx_active = true;
  181. furi_delay_ms(250);
  182. }
  183. bool hit = controller->hit_received;
  184. FURI_LOG_I(TAG, "Signal reception complete, hit received: %s", hit ? "true" : "false");
  185. controller->hit_received = false;
  186. return hit;
  187. }
  188. void infrared_controller_pause(InfraredController* controller) {
  189. if(controller->worker_rx_active) {
  190. FURI_LOG_I(TAG, "Stopping RX worker");
  191. infrared_worker_rx_stop(controller->worker);
  192. controller->worker_rx_active = false;
  193. }
  194. }
  195. void infrared_controller_resume(InfraredController* controller) {
  196. if(!controller->worker_rx_active) {
  197. FURI_LOG_I(TAG, "Starting RX worker");
  198. infrared_worker_rx_start(controller->worker);
  199. controller->worker_rx_active = true;
  200. }
  201. }