gps.c 11 KB

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