infrared_controller.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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_rx_callback(void* context, InfraredWorkerSignal* received_signal) {
  53. FURI_LOG_I(TAG, "RX callback triggered");
  54. InfraredController* controller = (InfraredController*)context;
  55. if(controller->processing_signal) {
  56. FURI_LOG_W(TAG, "Already processing a signal, skipping callback");
  57. return;
  58. }
  59. controller->processing_signal = true;
  60. if(!received_signal) {
  61. FURI_LOG_E(TAG, "Received signal is NULL");
  62. controller->processing_signal = false;
  63. return;
  64. }
  65. const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal);
  66. FURI_LOG_I(TAG, "Received signal - signal address: %p", (void*)received_signal);
  67. if(message) {
  68. FURI_LOG_I(
  69. TAG,
  70. "Received message: protocol=%d, address=0x%lx, command=0x%lx",
  71. message->protocol,
  72. (unsigned long)message->address,
  73. (unsigned long)message->command);
  74. if((controller->team == TeamRed && message->command == IR_COMMAND_BLUE_TEAM) ||
  75. (controller->team == TeamBlue && message->command == IR_COMMAND_RED_TEAM)) {
  76. controller->hit_received = true;
  77. FURI_LOG_I(
  78. TAG, "Hit detected for team: %s", controller->team == TeamRed ? "Red" : "Blue");
  79. notification_message_block(controller->notification, &sequence_hit);
  80. }
  81. } else {
  82. FURI_LOG_W(TAG, "RX callback received NULL message");
  83. }
  84. FURI_LOG_I(TAG, "RX callback completed");
  85. controller->processing_signal = false;
  86. }
  87. InfraredController* infrared_controller_alloc() {
  88. FURI_LOG_I(TAG, "Allocating InfraredController");
  89. InfraredController* controller = malloc(sizeof(InfraredController));
  90. if(!controller) {
  91. FURI_LOG_E(TAG, "Failed to allocate InfraredController");
  92. return NULL;
  93. }
  94. controller->team = TeamRed;
  95. controller->worker = infrared_worker_alloc();
  96. controller->signal = infrared_signal_alloc();
  97. controller->notification = furi_record_open(RECORD_NOTIFICATION);
  98. controller->hit_received = false;
  99. controller->processing_signal = false;
  100. if(controller->worker && controller->signal && controller->notification) {
  101. FURI_LOG_I(
  102. TAG, "InfraredWorker, InfraredSignal, and NotificationApp allocated successfully");
  103. } else {
  104. FURI_LOG_E(TAG, "Failed to allocate resources");
  105. free(controller);
  106. return NULL;
  107. }
  108. FURI_LOG_I(TAG, "Setting up RX callback");
  109. infrared_worker_rx_set_received_signal_callback(
  110. controller->worker, infrared_rx_callback, controller);
  111. FURI_LOG_I(TAG, "InfraredController allocated successfully");
  112. return controller;
  113. }
  114. void infrared_controller_free(InfraredController* controller) {
  115. FURI_LOG_I(TAG, "Freeing InfraredController");
  116. if(controller) {
  117. if(controller->worker_rx_active) {
  118. FURI_LOG_I(TAG, "Stopping RX worker");
  119. infrared_worker_rx_stop(controller->worker);
  120. }
  121. FURI_LOG_I(TAG, "Freeing InfraredWorker and InfraredSignal");
  122. infrared_worker_free(controller->worker);
  123. infrared_signal_free(controller->signal);
  124. FURI_LOG_I(TAG, "Closing NotificationApp");
  125. furi_record_close(RECORD_NOTIFICATION);
  126. free(controller);
  127. FURI_LOG_I(TAG, "InfraredController freed successfully");
  128. } else {
  129. FURI_LOG_W(TAG, "Attempted to free NULL InfraredController");
  130. }
  131. }
  132. void infrared_controller_set_team(InfraredController* controller, LaserTagTeam team) {
  133. FURI_LOG_I(TAG, "Setting team to %s", team == TeamRed ? "Red" : "Blue");
  134. controller->team = team;
  135. }
  136. void infrared_controller_send(InfraredController* controller) {
  137. FURI_LOG_I(TAG, "Preparing to send infrared signal");
  138. InfraredMessage message = {
  139. .protocol = InfraredProtocolNEC,
  140. .address = 0x42,
  141. .command = (controller->team == TeamRed) ? IR_COMMAND_RED_TEAM : IR_COMMAND_BLUE_TEAM};
  142. FURI_LOG_I(
  143. TAG,
  144. "Prepared message: protocol=%d, address=0x%lx, command=0x%lx",
  145. message.protocol,
  146. (unsigned long)message.address,
  147. (unsigned long)message.command);
  148. if(controller->worker_rx_active) {
  149. FURI_LOG_I(TAG, "Stopping RX worker");
  150. infrared_worker_rx_stop(controller->worker);
  151. controller->worker_rx_active = false;
  152. }
  153. FURI_LOG_I(TAG, "Setting message for infrared signal");
  154. infrared_signal_set_message(controller->signal, &message);
  155. FURI_LOG_I(TAG, "Starting infrared signal transmission");
  156. infrared_signal_transmit(controller->signal);
  157. if(!controller->worker_rx_active) {
  158. infrared_worker_rx_start(controller->worker);
  159. controller->worker_rx_active = true;
  160. }
  161. FURI_LOG_I(TAG, "Infrared signal transmission completed");
  162. }
  163. bool infrared_controller_receive(InfraredController* controller) {
  164. FURI_LOG_I(TAG, "Starting infrared signal reception");
  165. if(controller->processing_signal) {
  166. FURI_LOG_W(TAG, "Cannot start reception, another signal is still being processed");
  167. return false;
  168. }
  169. if(!controller->worker_rx_active) {
  170. infrared_worker_rx_start(controller->worker);
  171. controller->worker_rx_active = true;
  172. furi_delay_ms(250);
  173. }
  174. bool hit = controller->hit_received;
  175. FURI_LOG_I(TAG, "Signal reception complete, hit received: %s", hit ? "true" : "false");
  176. controller->hit_received = false;
  177. return hit;
  178. }
  179. void infrared_controller_pause(InfraredController* controller) {
  180. if(controller->worker_rx_active) {
  181. FURI_LOG_I(TAG, "Stopping RX worker");
  182. infrared_worker_rx_stop(controller->worker);
  183. controller->worker_rx_active = false;
  184. }
  185. }
  186. void infrared_controller_resume(InfraredController* controller) {
  187. if(!controller->worker_rx_active) {
  188. FURI_LOG_I(TAG, "Starting RX worker");
  189. infrared_worker_rx_start(controller->worker);
  190. controller->worker_rx_active = true;
  191. }
  192. }