air_mouse_app.c 7.7 KB

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