air_mouse_app.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <furi_hal_bt.h>
  4. #include <extra_profiles/hid_profile.h>
  5. #include <bt/bt_service/bt.h>
  6. #include <gui/gui.h>
  7. #include <gui/view_dispatcher.h>
  8. #include <gui/modules/submenu.h>
  9. #include <gui/modules/dialog_ex.h>
  10. #include "imu_mouse.h"
  11. #include "views/air_mouse_view.h"
  12. #include <furi_hal_usb_hid.h>
  13. #include <storage/storage.h>
  14. #define TAG "SensorModule"
  15. #define HID_BT_KEYS_STORAGE_NAME ".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. FuriHalBleProfileBase* ble_hid_profile;
  27. } AirMouseApp;
  28. typedef enum {
  29. AirMouseViewError,
  30. AirMouseViewStartSubmenu,
  31. AirMouseViewMain,
  32. } AirMouseViews;
  33. enum StertSubmenuIndex {
  34. StartSubmenuIndexUsb,
  35. StartSubmenuIndexBle,
  36. StartSubmenuIndexBleReset,
  37. };
  38. static const BleProfileHidParams ble_hid_params = {
  39. .device_name_prefix = "AirMouse",
  40. .mac_xor = 0x0001,
  41. };
  42. static bool usb_hid_mouse_move(void* inst, int8_t dx, int8_t dy) {
  43. UNUSED(inst);
  44. return furi_hal_hid_mouse_move(dx, dy);
  45. }
  46. static bool usb_hid_mouse_key_press(void* inst, uint8_t button) {
  47. UNUSED(inst);
  48. return furi_hal_hid_mouse_press(button);
  49. }
  50. static bool usb_hid_mouse_key_release(void* inst, uint8_t button) {
  51. UNUSED(inst);
  52. return furi_hal_hid_mouse_release(button);
  53. }
  54. static bool usb_hid_mouse_scroll(void* inst, int8_t value) {
  55. UNUSED(inst);
  56. return furi_hal_hid_mouse_scroll(value);
  57. }
  58. static const ImuHidApi hid_api_usb = {
  59. .mouse_move = usb_hid_mouse_move,
  60. .mouse_key_press = usb_hid_mouse_key_press,
  61. .mouse_key_release = usb_hid_mouse_key_release,
  62. .mouse_scroll = usb_hid_mouse_scroll,
  63. .report_rate_max = 200,
  64. };
  65. static bool ble_hid_mouse_move(void* inst, int8_t dx, int8_t dy) {
  66. return ble_profile_hid_mouse_move(inst, dx, dy);
  67. }
  68. static bool ble_hid_mouse_key_press(void* inst, uint8_t button) {
  69. return ble_profile_hid_mouse_press(inst, button);
  70. }
  71. static bool ble_hid_mouse_key_release(void* inst, uint8_t button) {
  72. return ble_profile_hid_mouse_release(inst, button);
  73. }
  74. static bool ble_hid_mouse_scroll(void* inst, int8_t value) {
  75. return ble_profile_hid_mouse_scroll(inst, value);
  76. }
  77. static const ImuHidApi hid_api_ble = {
  78. .mouse_move = ble_hid_mouse_move,
  79. .mouse_key_press = ble_hid_mouse_key_press,
  80. .mouse_key_release = ble_hid_mouse_key_release,
  81. .mouse_scroll = ble_hid_mouse_scroll,
  82. .report_rate_max = 30,
  83. };
  84. static void ble_hid_remove_pairing(void) {
  85. Bt* bt = furi_record_open(RECORD_BT);
  86. bt_disconnect(bt);
  87. // Wait 2nd core to update nvm storage
  88. furi_delay_ms(200);
  89. furi_hal_bt_stop_advertising();
  90. bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
  91. bt_forget_bonded_devices(bt);
  92. // Wait 2nd core to update nvm storage
  93. furi_delay_ms(200);
  94. bt_keys_storage_set_default_path(bt);
  95. furi_check(bt_profile_restore_default(bt));
  96. furi_record_close(RECORD_BT);
  97. }
  98. static void ble_hid_connection_status_callback(BtStatus status, void* context) {
  99. furi_assert(context);
  100. AirMouseApp* app = context;
  101. bool connected = (status == BtStatusConnected);
  102. air_mouse_view_set_connected_status(app->air_mouse_view, connected);
  103. }
  104. static FuriHalBleProfileBase* ble_hid_init(AirMouseApp* app) {
  105. app->bt = furi_record_open(RECORD_BT);
  106. bt_disconnect(app->bt);
  107. // Wait 2nd core to update nvm storage
  108. furi_delay_ms(200);
  109. bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
  110. FuriHalBleProfileBase* ble_hid_profile =
  111. bt_profile_start(app->bt, ble_profile_hid, (void*)&ble_hid_params);
  112. furi_check(ble_hid_profile);
  113. furi_hal_bt_start_advertising();
  114. bt_set_status_changed_callback(app->bt, ble_hid_connection_status_callback, app);
  115. return ble_hid_profile;
  116. }
  117. static void ble_hid_deinit(AirMouseApp* app) {
  118. bt_set_status_changed_callback(app->bt, NULL, NULL);
  119. bt_disconnect(app->bt);
  120. // Wait 2nd core to update nvm storage
  121. furi_delay_ms(200);
  122. bt_keys_storage_set_default_path(app->bt);
  123. furi_check(bt_profile_restore_default(app->bt));
  124. furi_record_close(RECORD_BT);
  125. app->bt = NULL;
  126. }
  127. static uint32_t air_mouse_exit(void* context) {
  128. UNUSED(context);
  129. return VIEW_NONE;
  130. }
  131. static uint32_t air_mouse_return_to_menu(void* context) {
  132. UNUSED(context);
  133. return AirMouseViewStartSubmenu;
  134. }
  135. static void air_mouse_hid_deinit(void* context) {
  136. furi_assert(context);
  137. AirMouseApp* app = context;
  138. if(app->bt) {
  139. ble_hid_deinit(app);
  140. app->ble_hid_profile = NULL;
  141. } else if(app->usb_mode_prev) {
  142. furi_hal_usb_set_config(app->usb_mode_prev, NULL);
  143. app->usb_mode_prev = NULL;
  144. }
  145. }
  146. static void air_mouse_submenu_callback(void* context, uint32_t index) {
  147. furi_assert(context);
  148. AirMouseApp* app = context;
  149. if(index == StartSubmenuIndexUsb) {
  150. app->usb_mode_prev = furi_hal_usb_get_config();
  151. furi_hal_usb_unlock();
  152. furi_hal_usb_set_config(&usb_hid, NULL);
  153. air_mouse_view_set_hid_api(app->air_mouse_view, &hid_api_usb, NULL, false);
  154. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewMain);
  155. } else if(index == StartSubmenuIndexBle) {
  156. app->ble_hid_profile = ble_hid_init(app);
  157. air_mouse_view_set_hid_api(app->air_mouse_view, &hid_api_ble, app->ble_hid_profile, true);
  158. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewMain);
  159. } else if(index == StartSubmenuIndexBleReset) {
  160. ble_hid_remove_pairing();
  161. }
  162. }
  163. static AirMouseApp* air_mouse_alloc(void) {
  164. AirMouseApp* app = malloc(sizeof(AirMouseApp));
  165. app->gui = furi_record_open(RECORD_GUI);
  166. app->view_dispatcher = view_dispatcher_alloc();
  167. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  168. app->air_mouse_view = air_mouse_view_alloc(air_mouse_hid_deinit, app);
  169. view_set_previous_callback(
  170. air_mouse_view_get_view(app->air_mouse_view), air_mouse_return_to_menu);
  171. view_dispatcher_add_view(
  172. app->view_dispatcher, AirMouseViewMain, air_mouse_view_get_view(app->air_mouse_view));
  173. app->start_submenu = submenu_alloc();
  174. submenu_add_item(
  175. app->start_submenu, "USB Remote", StartSubmenuIndexUsb, air_mouse_submenu_callback, app);
  176. submenu_add_item(
  177. app->start_submenu,
  178. "Bluetooth Remote",
  179. StartSubmenuIndexBle,
  180. air_mouse_submenu_callback,
  181. app);
  182. submenu_add_item(
  183. app->start_submenu,
  184. "Remove Pairing",
  185. StartSubmenuIndexBleReset,
  186. air_mouse_submenu_callback,
  187. app);
  188. view_set_previous_callback(submenu_get_view(app->start_submenu), air_mouse_exit);
  189. view_dispatcher_add_view(
  190. app->view_dispatcher, AirMouseViewStartSubmenu, submenu_get_view(app->start_submenu));
  191. app->error_dialog = dialog_ex_alloc();
  192. dialog_ex_set_header(app->error_dialog, "Sensor Module error", 63, 0, AlignCenter, AlignTop);
  193. dialog_ex_set_text(app->error_dialog, "Module not conntected", 63, 30, AlignCenter, AlignTop);
  194. view_set_previous_callback(dialog_ex_get_view(app->error_dialog), air_mouse_exit);
  195. view_dispatcher_add_view(
  196. app->view_dispatcher, AirMouseViewError, dialog_ex_get_view(app->error_dialog));
  197. return app;
  198. }
  199. static void air_mouse_free(AirMouseApp* app) {
  200. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewStartSubmenu);
  201. submenu_free(app->start_submenu);
  202. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewError);
  203. dialog_ex_free(app->error_dialog);
  204. view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewMain);
  205. air_mouse_view_free(app->air_mouse_view);
  206. view_dispatcher_free(app->view_dispatcher);
  207. furi_record_close(RECORD_GUI);
  208. free(app);
  209. }
  210. int32_t air_mouse_app(void* arg) {
  211. UNUSED(arg);
  212. AirMouseApp* app = air_mouse_alloc();
  213. app->icm42688p_device = malloc(sizeof(FuriHalSpiBusHandle));
  214. memcpy(app->icm42688p_device, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle));
  215. app->icm42688p_device->cs = &gpio_ext_pc3;
  216. app->icm42688p = icm42688p_alloc(app->icm42688p_device, &gpio_ext_pb2);
  217. bool icm42688p_valid = icm42688p_init(app->icm42688p);
  218. if(icm42688p_valid) {
  219. air_mouse_view_set_device(app->air_mouse_view, app->icm42688p);
  220. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewStartSubmenu);
  221. } else {
  222. view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewError);
  223. }
  224. view_dispatcher_run(app->view_dispatcher);
  225. if(!icm42688p_deinit(app->icm42688p)) {
  226. FURI_LOG_E(TAG, "Failed to deinitialize ICM42688P");
  227. }
  228. icm42688p_free(app->icm42688p);
  229. free(app->icm42688p_device);
  230. air_mouse_free(app);
  231. return 0;
  232. }