air_mouse_app.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <furi_hal_bt_hid.h>
  4. #include <bt/bt_service/bt.h>
  5. #include <gui/gui.h>
  6. #include <gui/view_dispatcher.h>
  7. #include <gui/modules/submenu.h>
  8. #include <gui/modules/dialog_ex.h>
  9. #include "imu_mouse.h"
  10. #include "views/air_mouse_view.h"
  11. #include <furi_hal_usb_hid.h>
  12. #include <storage/storage.h>
  13. #define TAG "SensorModule"
  14. #define BLE_HID_KEYS_PATH "/ext/apps_data/hid_ble/.bt_hid.keys"
  15. typedef struct {
  16. Gui* gui;
  17. ViewDispatcher* view_dispatcher;
  18. Submenu* start_submenu;
  19. DialogEx* error_dialog;
  20. AirMouseView* air_mouse_view;
  21. FuriHalSpiBusHandle* icm42688p_device;
  22. ICM42688P* icm42688p;
  23. FuriHalUsbInterface* usb_mode_prev;
  24. Bt* bt;
  25. } AirMouseApp;
  26. typedef enum {
  27. AirMouseViewError,
  28. AirMouseViewStartSubmenu,
  29. AirMouseViewMain,
  30. } AirMouseViews;
  31. enum StertSubmenuIndex {
  32. StartSubmenuIndexUsb,
  33. StartSubmenuIndexBle,
  34. StartSubmenuIndexBleReset,
  35. };
  36. static const ImuHidApi hid_api_usb = {
  37. .mouse_move = furi_hal_hid_mouse_move,
  38. .mouse_key_press = furi_hal_hid_mouse_press,
  39. .mouse_key_release = furi_hal_hid_mouse_release,
  40. .mouse_scroll = furi_hal_hid_mouse_scroll,
  41. .report_rate_max = 200,
  42. };
  43. static const ImuHidApi hid_api_ble = {
  44. .mouse_move = furi_hal_bt_hid_mouse_move,
  45. .mouse_key_press = furi_hal_bt_hid_mouse_press,
  46. .mouse_key_release = furi_hal_bt_hid_mouse_release,
  47. .mouse_scroll = furi_hal_bt_hid_mouse_scroll,
  48. .report_rate_max = 30,
  49. };
  50. static void ble_hid_remove_pairing(void) {
  51. Bt* bt = furi_record_open(RECORD_BT);
  52. bt_disconnect(bt);
  53. // Wait 2nd core to update nvm storage
  54. furi_delay_ms(200);
  55. bt_keys_storage_set_storage_path(bt, BLE_HID_KEYS_PATH);
  56. bt_forget_bonded_devices(bt);
  57. // Wait 2nd core to update nvm storage
  58. furi_delay_ms(200);
  59. bt_keys_storage_set_default_path(bt);
  60. furi_check(bt_set_profile(bt, BtProfileSerial));
  61. furi_record_close(RECORD_BT);
  62. }
  63. static void ble_hid_connection_status_callback(BtStatus status, void* context) {
  64. furi_assert(context);
  65. AirMouseApp* app = context;
  66. bool connected = (status == BtStatusConnected);
  67. air_mouse_view_set_connected_status(app->air_mouse_view, connected);
  68. }
  69. static Bt* ble_hid_init(AirMouseApp* app) {
  70. Bt* bt = furi_record_open(RECORD_BT);
  71. bt_disconnect(bt);
  72. // Wait 2nd core to update nvm storage
  73. furi_delay_ms(200);
  74. bt_keys_storage_set_storage_path(bt, BLE_HID_KEYS_PATH);
  75. furi_check(bt_set_profile(bt, BtProfileHidKeyboard));
  76. furi_hal_bt_start_advertising();
  77. bt_set_status_changed_callback(bt, ble_hid_connection_status_callback, app);
  78. return bt;
  79. }
  80. static void ble_hid_deinit(Bt* bt) {
  81. bt_set_status_changed_callback(bt, NULL, NULL);
  82. bt_disconnect(bt);
  83. // Wait 2nd core to update nvm storage
  84. furi_delay_ms(200);
  85. bt_keys_storage_set_default_path(bt);
  86. furi_check(bt_set_profile(bt, BtProfileSerial));
  87. furi_record_close(RECORD_BT);
  88. }
  89. static uint32_t air_mouse_exit(void* context) {
  90. UNUSED(context);
  91. return VIEW_NONE;
  92. }
  93. static uint32_t air_mouse_return_to_menu(void* context) {
  94. UNUSED(context);
  95. return AirMouseViewStartSubmenu;
  96. }
  97. static void air_mouse_hid_deinit(void* context) {
  98. furi_assert(context);
  99. AirMouseApp* app = context;
  100. if(app->bt) {
  101. ble_hid_deinit(app->bt);
  102. app->bt = NULL;
  103. } else if(app->usb_mode_prev) {
  104. furi_hal_usb_set_config(app->usb_mode_prev, NULL);
  105. app->usb_mode_prev = NULL;
  106. }
  107. }
  108. static void air_mouse_submenu_callback(void* context, uint32_t index) {
  109. furi_assert(context);
  110. AirMouseApp* app = context;
  111. if(index == StartSubmenuIndexUsb) {
  112. app->usb_mode_prev = furi_hal_usb_get_config();
  113. furi_hal_usb_unlock();
  114. furi_hal_usb_set_config(&usb_hid, NULL);
  115. air_mouse_view_set_hid_api(app->air_mouse_view, &hid_api_usb, false);
  116. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewMain);
  117. } else if(index == StartSubmenuIndexBle) {
  118. app->bt = ble_hid_init(app);
  119. air_mouse_view_set_hid_api(app->air_mouse_view, &hid_api_ble, true);
  120. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewMain);
  121. } else if(index == StartSubmenuIndexBleReset) {
  122. ble_hid_remove_pairing();
  123. }
  124. }
  125. static AirMouseApp* air_mouse_alloc(void) {
  126. AirMouseApp* app = malloc(sizeof(AirMouseApp));
  127. app->gui = furi_record_open(RECORD_GUI);
  128. app->view_dispatcher = view_dispatcher_alloc();
  129. view_dispatcher_enable_queue(app->view_dispatcher);
  130. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  131. app->air_mouse_view = air_mouse_view_alloc(air_mouse_hid_deinit, app);
  132. view_set_previous_callback(
  133. air_mouse_view_get_view(app->air_mouse_view), air_mouse_return_to_menu);
  134. view_dispatcher_add_view(
  135. app->view_dispatcher, AirMouseViewMain, air_mouse_view_get_view(app->air_mouse_view));
  136. app->start_submenu = submenu_alloc();
  137. submenu_add_item(
  138. app->start_submenu, "USB Remote", StartSubmenuIndexUsb, air_mouse_submenu_callback, app);
  139. submenu_add_item(
  140. app->start_submenu,
  141. "Bluetooth Remote",
  142. StartSubmenuIndexBle,
  143. air_mouse_submenu_callback,
  144. app);
  145. submenu_add_item(
  146. app->start_submenu,
  147. "Remove Pairing",
  148. StartSubmenuIndexBleReset,
  149. air_mouse_submenu_callback,
  150. app);
  151. view_set_previous_callback(submenu_get_view(app->start_submenu), air_mouse_exit);
  152. view_dispatcher_add_view(
  153. app->view_dispatcher, AirMouseViewStartSubmenu, submenu_get_view(app->start_submenu));
  154. app->error_dialog = dialog_ex_alloc();
  155. dialog_ex_set_header(app->error_dialog, "Sensor Module error", 63, 0, AlignCenter, AlignTop);
  156. dialog_ex_set_text(app->error_dialog, "Module not conntected", 63, 30, AlignCenter, AlignTop);
  157. view_set_previous_callback(dialog_ex_get_view(app->error_dialog), air_mouse_exit);
  158. view_dispatcher_add_view(
  159. app->view_dispatcher, AirMouseViewError, dialog_ex_get_view(app->error_dialog));
  160. return app;
  161. }
  162. static void air_mouse_free(AirMouseApp* app) {
  163. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewStartSubmenu);
  164. submenu_free(app->start_submenu);
  165. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewError);
  166. dialog_ex_free(app->error_dialog);
  167. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewMain);
  168. air_mouse_view_free(app->air_mouse_view);
  169. view_dispatcher_free(app->view_dispatcher);
  170. furi_record_close(RECORD_GUI);
  171. free(app);
  172. }
  173. int32_t air_mouse_app(void* arg) {
  174. UNUSED(arg);
  175. AirMouseApp* app = air_mouse_alloc();
  176. app->icm42688p_device = malloc(sizeof(FuriHalSpiBusHandle));
  177. memcpy(app->icm42688p_device, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle));
  178. app->icm42688p_device->cs = &gpio_ext_pc3;
  179. app->icm42688p = icm42688p_alloc(app->icm42688p_device, &gpio_ext_pb2);
  180. bool icm42688p_valid = icm42688p_init(app->icm42688p);
  181. if(icm42688p_valid) {
  182. air_mouse_view_set_device(app->air_mouse_view, app->icm42688p);
  183. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewStartSubmenu);
  184. } else {
  185. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewError);
  186. }
  187. view_dispatcher_run(app->view_dispatcher);
  188. if(!icm42688p_deinit(app->icm42688p)) {
  189. FURI_LOG_E(TAG, "Failed to deinitialize ICM42688P");
  190. }
  191. icm42688p_free(app->icm42688p);
  192. free(app->icm42688p_device);
  193. air_mouse_free(app);
  194. return 0;
  195. }