gps.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include "gps_uart.h"
  2. #include "constants.h"
  3. #include <furi.h>
  4. #include <gui/gui.h>
  5. #include <string.h>
  6. typedef enum {
  7. EventTypeTick,
  8. EventTypeKey,
  9. } EventType;
  10. typedef struct {
  11. EventType type;
  12. InputEvent input;
  13. } PluginEvent;
  14. static void render_callback(Canvas* const canvas, void* context) {
  15. furi_assert(context);
  16. GpsUart* gps_uart = context;
  17. furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
  18. char buffer[64];
  19. switch(gps_uart->view_state) {
  20. case CHANGE_BAUDRATE:
  21. canvas_set_font(canvas, FontPrimary);
  22. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "Baudrate set to:");
  23. snprintf(buffer, 64, "%ld baud", gps_uart->baudrate);
  24. canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, buffer);
  25. break;
  26. case CHANGE_BACKLIGHT:
  27. canvas_set_font(canvas, FontPrimary);
  28. canvas_draw_str_aligned(
  29. canvas,
  30. 64,
  31. 32,
  32. AlignCenter,
  33. AlignBottom,
  34. gps_uart->backlight_enabled ? "Backlight enabled" : "Backlight disabled");
  35. break;
  36. case CHANGE_DEEPSLEEP:
  37. canvas_set_font(canvas, FontPrimary);
  38. canvas_draw_str_aligned(
  39. canvas,
  40. 64,
  41. 32,
  42. AlignCenter,
  43. AlignBottom,
  44. gps_uart->deep_sleep_enabled ? "Deep sleep enabled" : "Deep sleep disabled");
  45. break;
  46. case CHANGE_SPEEDUNIT:
  47. canvas_set_font(canvas, FontPrimary);
  48. canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "Speed unit set to:");
  49. switch(gps_uart->speed_units) {
  50. case KPH:
  51. canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, "km/h");
  52. break;
  53. case MPH:
  54. canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, "mi/h");
  55. break;
  56. case KNOTS:
  57. default:
  58. canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, "kn");
  59. break;
  60. }
  61. break;
  62. case NORMAL:
  63. default:
  64. canvas_set_font(canvas, FontPrimary);
  65. canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude");
  66. canvas_draw_str_aligned(canvas, 96, 8, AlignCenter, AlignBottom, "Longitude");
  67. canvas_draw_str_aligned(canvas, 21, 30, AlignCenter, AlignBottom, "Course");
  68. canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignBottom, "Speed");
  69. canvas_draw_str_aligned(canvas, 107, 30, AlignCenter, AlignBottom, "Altitude");
  70. canvas_draw_str_aligned(canvas, 32, 52, AlignCenter, AlignBottom, "Satellites");
  71. canvas_draw_str_aligned(canvas, 96, 52, AlignCenter, AlignBottom, "Last Fix");
  72. canvas_set_font(canvas, FontSecondary);
  73. snprintf(buffer, 64, "%f", (double)gps_uart->status.latitude);
  74. canvas_draw_str_aligned(canvas, 32, 18, AlignCenter, AlignBottom, buffer);
  75. snprintf(buffer, 64, "%f", (double)gps_uart->status.longitude);
  76. canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer);
  77. snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course);
  78. canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer);
  79. switch(gps_uart->speed_units) {
  80. case KPH:
  81. snprintf(buffer, 64, "%.2f km/h", (double)(gps_uart->status.speed * KNOTS_TO_KPH));
  82. break;
  83. case MPH:
  84. snprintf(buffer, 64, "%.2f mi/h", (double)(gps_uart->status.speed * KNOTS_TO_MPH));
  85. break;
  86. case KNOTS:
  87. default:
  88. snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed);
  89. break;
  90. }
  91. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer);
  92. snprintf(
  93. buffer,
  94. 64,
  95. "%.1f %c",
  96. (double)gps_uart->status.altitude,
  97. tolower(gps_uart->status.altitude_units));
  98. canvas_draw_str_aligned(canvas, 107, 40, AlignCenter, AlignBottom, buffer);
  99. snprintf(buffer, 64, "%d", gps_uart->status.satellites_tracked);
  100. canvas_draw_str_aligned(canvas, 32, 62, AlignCenter, AlignBottom, buffer);
  101. snprintf(
  102. buffer,
  103. 64,
  104. "%02d:%02d:%02d UTC",
  105. gps_uart->status.time_hours,
  106. gps_uart->status.time_minutes,
  107. gps_uart->status.time_seconds);
  108. canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer);
  109. break;
  110. }
  111. furi_mutex_release(gps_uart->mutex);
  112. }
  113. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  114. furi_assert(event_queue);
  115. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  116. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  117. }
  118. int32_t gps_app(void* p) {
  119. UNUSED(p);
  120. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  121. GpsUart* gps_uart = gps_uart_enable();
  122. gps_uart->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  123. if(!gps_uart->mutex) {
  124. FURI_LOG_E("GPS", "cannot create mutex\r\n");
  125. free(gps_uart);
  126. return 255;
  127. }
  128. uint8_t attempts = 0;
  129. bool otg_was_enabled = furi_hal_power_is_otg_enabled();
  130. while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
  131. furi_hal_power_enable_otg();
  132. furi_delay_ms(10);
  133. }
  134. furi_delay_ms(200);
  135. // set system callbacks
  136. ViewPort* view_port = view_port_alloc();
  137. view_port_draw_callback_set(view_port, render_callback, gps_uart);
  138. view_port_input_callback_set(view_port, input_callback, event_queue);
  139. // open GUI and register view_port
  140. Gui* gui = furi_record_open(RECORD_GUI);
  141. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  142. PluginEvent event;
  143. for(bool processing = true; processing;) {
  144. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  145. furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
  146. if(event_status == FuriStatusOk) {
  147. // press events
  148. if(event.type == EventTypeKey) {
  149. if(event.input.type == InputTypeShort) {
  150. switch(event.input.key) {
  151. case InputKeyBack:
  152. processing = false;
  153. break;
  154. case InputKeyOk:
  155. if(!gps_uart->backlight_enabled) {
  156. notification_message_block(
  157. gps_uart->notifications, &sequence_display_backlight_enforce_on);
  158. gps_uart->backlight_enabled = true;
  159. } else {
  160. notification_message_block(
  161. gps_uart->notifications, &sequence_display_backlight_enforce_auto);
  162. notification_message(
  163. gps_uart->notifications, &sequence_display_backlight_off);
  164. gps_uart->backlight_enabled = false;
  165. }
  166. gps_uart->view_state = CHANGE_BACKLIGHT;
  167. furi_mutex_release(gps_uart->mutex);
  168. view_port_update(view_port);
  169. furi_delay_ms(1000);
  170. gps_uart->view_state = NORMAL;
  171. break;
  172. default:
  173. break;
  174. }
  175. } else if(event.input.type == InputTypeLong) {
  176. switch(event.input.key) {
  177. case InputKeyUp:
  178. gps_uart_deinit_thread(gps_uart);
  179. const int baudrate_length =
  180. sizeof(gps_baudrates) / sizeof(gps_baudrates[0]);
  181. current_gps_baudrate++;
  182. if(current_gps_baudrate >= baudrate_length) {
  183. current_gps_baudrate = 0;
  184. }
  185. gps_uart->baudrate = gps_baudrates[current_gps_baudrate];
  186. gps_uart_init_thread(gps_uart);
  187. gps_uart->view_state = CHANGE_BAUDRATE;
  188. furi_mutex_release(gps_uart->mutex);
  189. view_port_update(view_port);
  190. furi_delay_ms(1000);
  191. gps_uart->view_state = NORMAL;
  192. break;
  193. case InputKeyRight:
  194. gps_uart->speed_units++;
  195. if(gps_uart->speed_units == INVALID) {
  196. gps_uart->speed_units = KNOTS;
  197. }
  198. gps_uart->view_state = CHANGE_SPEEDUNIT;
  199. furi_mutex_release(gps_uart->mutex);
  200. view_port_update(view_port);
  201. furi_delay_ms(1000);
  202. gps_uart->view_state = NORMAL;
  203. break;
  204. case InputKeyDown:
  205. gps_uart->view_state = CHANGE_DEEPSLEEP;
  206. gps_uart->deep_sleep_enabled = !gps_uart->deep_sleep_enabled;
  207. // tested on Telit SE868-A and SL871L-S
  208. furi_hal_serial_tx(
  209. gps_uart->serial_handle,
  210. (uint8_t*)"$PMTK161,0*28\r\n",
  211. strlen("$PMTK161,0*28\r\n"));
  212. furi_mutex_release(gps_uart->mutex);
  213. view_port_update(view_port);
  214. furi_delay_ms(1000);
  215. gps_uart->view_state = NORMAL;
  216. break;
  217. case InputKeyBack:
  218. processing = false;
  219. break;
  220. default:
  221. break;
  222. }
  223. }
  224. }
  225. }
  226. if(gps_uart->view_state == NORMAL) {
  227. furi_mutex_release(gps_uart->mutex);
  228. view_port_update(view_port);
  229. }
  230. }
  231. notification_message_block(gps_uart->notifications, &sequence_display_backlight_enforce_auto);
  232. view_port_enabled_set(view_port, false);
  233. gui_remove_view_port(gui, view_port);
  234. furi_record_close(RECORD_GUI);
  235. view_port_free(view_port);
  236. furi_message_queue_free(event_queue);
  237. furi_mutex_free(gps_uart->mutex);
  238. gps_uart_disable(gps_uart);
  239. if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
  240. furi_hal_power_disable_otg();
  241. }
  242. return 0;
  243. }