gpio_simple_motor.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include "gpio_simple_motor.h"
  2. #include "../fbp.h"
  3. static const uint16_t BT_SERIAL_BUFFER_SIZE = 128;
  4. static const uint32_t DEFAULT_FREQ = 1000;
  5. static const FuriHalPwmOutputId DEFAULT_PWM_OUTPUT_ID = FuriHalPwmOutputIdTim1PA7;
  6. struct GPIOSimpleMotor {
  7. View* view;
  8. FBP* fbp;
  9. uint8_t current_pwm_duty;
  10. };
  11. typedef struct {
  12. char* display_text_1;
  13. char* display_text_2;
  14. char* display_text_3;
  15. } GPIOSimpleMotorModel;
  16. static void process_general_command(TCodeCommand command, GPIOSimpleMotor* motor) {
  17. if(command.command_type == Magnitude &&
  18. command.data.magnitude_command.motion_type == Vibrate &&
  19. command.data.magnitude_command.channel_id == 0) {
  20. // just enable vibration on X
  21. uint8_t new_duty = (uint8_t)(command.data.magnitude_command.magnitude * 100);
  22. if(new_duty > 100) {
  23. new_duty = 100;
  24. }
  25. FURI_LOG_D(TAG, "Setting vibration power on %u", new_duty);
  26. // using Pulse-Widht Modulation to control a motor via a transistor
  27. // just google for a typical arduino + PWM + motor scheme
  28. if(new_duty == 0) {
  29. if(furi_hal_pwm_is_running(DEFAULT_PWM_OUTPUT_ID)) {
  30. furi_hal_pwm_stop(DEFAULT_PWM_OUTPUT_ID);
  31. }
  32. } else if(motor->current_pwm_duty == 0) {
  33. if(!furi_hal_pwm_is_running(DEFAULT_PWM_OUTPUT_ID)) {
  34. furi_hal_pwm_start(DEFAULT_PWM_OUTPUT_ID, DEFAULT_FREQ, new_duty);
  35. }
  36. } else {
  37. furi_hal_pwm_set_params(DEFAULT_PWM_OUTPUT_ID, DEFAULT_FREQ, new_duty);
  38. }
  39. motor->current_pwm_duty = new_duty;
  40. return;
  41. }
  42. }
  43. static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) {
  44. furi_assert(context);
  45. GPIOSimpleMotor* motor = context;
  46. if(event.event == SerialServiceEventTypeDataReceived) {
  47. TCodeCommandArray commands = tcode_decode(event.data.buffer, event.data.size);
  48. FURI_LOG_D(TAG, "Decoded commands array size: %u", commands.size);
  49. for(uint16_t i = 0; i < commands.size; i++) {
  50. FURI_LOG_D(TAG, "Command #%u, type: %u\n", i, commands.commands[i].command_type);
  51. }
  52. for(uint16_t i = 0; i < commands.size; i++) {
  53. // looking for first vibro command to execute
  54. TCodeCommand current_command = commands.commands[i];
  55. TCodeCommandType type = current_command.command_type;
  56. if((type == Magnitude || type == MagnitudeSpeed || type == MagnitudeTimeInterval)) {
  57. process_general_command(current_command, motor);
  58. }
  59. }
  60. }
  61. return 0;
  62. }
  63. static bool input_callback(InputEvent* event, void* ctx) {
  64. furi_assert(ctx);
  65. GPIOSimpleMotor* motor = ctx;
  66. if(event->key == InputKeyBack) {
  67. furi_hal_bt_serial_set_event_callback(0, NULL, NULL);
  68. return false;
  69. }
  70. if(event->key == InputKeyOk) {
  71. if(furi_hal_bt_is_active()) {
  72. FURI_LOG_D(TAG, "BT is working, hijacking the serial connection...");
  73. furi_hal_bt_start_advertising();
  74. furi_hal_bt_serial_set_event_callback(
  75. BT_SERIAL_BUFFER_SIZE, bt_serial_event_callback, motor);
  76. with_view_model(
  77. motor->view,
  78. GPIOSimpleMotorModel * model,
  79. {
  80. model->display_text_1 = "";
  81. model->display_text_2 = "Ready ^_^";
  82. model->display_text_3 = "";
  83. },
  84. true);
  85. } else {
  86. FURI_LOG_E(TAG, "Please, enable the Bluetooth and restart the app");
  87. with_view_model(
  88. motor->view,
  89. GPIOSimpleMotorModel * model,
  90. {
  91. model->display_text_1 = "Error:";
  92. model->display_text_2 = "Bluetooth is not enabled";
  93. model->display_text_3 = "";
  94. },
  95. true);
  96. }
  97. }
  98. return true;
  99. }
  100. static void draw_callback(Canvas* canvas, void* ctx) {
  101. furi_assert(ctx);
  102. GPIOSimpleMotorModel* app = ctx;
  103. canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, (char*)app->display_text_1);
  104. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, (char*)app->display_text_2);
  105. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, (char*)app->display_text_3);
  106. }
  107. GPIOSimpleMotor* gpio_simple_motor_alloc(FBP* fbp) {
  108. furi_assert(fbp);
  109. GPIOSimpleMotor* motor = malloc(sizeof(GPIOSimpleMotor));
  110. motor->view = view_alloc();
  111. motor->fbp = fbp;
  112. view_set_context(motor->view, motor);
  113. view_allocate_model(motor->view, ViewModelTypeLocking, sizeof(GPIOSimpleMotorModel));
  114. view_set_draw_callback(motor->view, draw_callback);
  115. view_set_input_callback(motor->view, input_callback);
  116. with_view_model(
  117. motor->view,
  118. GPIOSimpleMotorModel * model,
  119. {
  120. model->display_text_1 = "Please, connect the";
  121. model->display_text_2 = "transistor base to pin A7!";
  122. model->display_text_3 = "Press OK to start";
  123. },
  124. true);
  125. return motor;
  126. }
  127. void gpio_simple_motor_free(GPIOSimpleMotor* motor) {
  128. furi_assert(motor);
  129. if(furi_hal_pwm_is_running(DEFAULT_PWM_OUTPUT_ID)) {
  130. furi_hal_pwm_stop(DEFAULT_PWM_OUTPUT_ID);
  131. }
  132. view_free(motor->view);
  133. free(motor);
  134. }
  135. View* gpio_simple_motor_get_view(GPIOSimpleMotor* motor) {
  136. furi_assert(motor);
  137. return motor->view;
  138. }