Skip to content

Commit fe98f61

Browse files
committed
Import and FAP conversion of GPS plugin from flipperzero-firmware fork. Does not build yet as stream_buffer.h API is not yet available for FAP.
1 parent 2feee6c commit fe98f61

File tree

6 files changed

+1240
-0
lines changed

6 files changed

+1240
-0
lines changed

src/application.fam

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
App(
2+
appid="gps",
3+
name="GPS",
4+
apptype=FlipperAppType.EXTERNAL,
5+
entry_point="gps_app",
6+
cdefines=["APP_GPS"],
7+
requires=[
8+
"gui",
9+
],
10+
stack_size=1 * 1024,
11+
order=20,
12+
)

src/gps_app.c

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#include "gps_uart.h"
2+
3+
#include <furi.h>
4+
#include <gui/gui.h>
5+
#include <string.h>
6+
7+
typedef enum {
8+
EventTypeTick,
9+
EventTypeKey,
10+
} EventType;
11+
12+
typedef struct {
13+
EventType type;
14+
InputEvent input;
15+
} PluginEvent;
16+
17+
static void render_callback(Canvas* const canvas, void* context) {
18+
const GpsUart* gps_uart = acquire_mutex((ValueMutex*)context, 25);
19+
if(gps_uart == NULL) {
20+
return;
21+
}
22+
23+
canvas_set_font(canvas, FontSecondary);
24+
char buffer[64];
25+
snprintf(buffer, 64, "LAT: %f", (double)gps_uart->status.latitude);
26+
canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, buffer);
27+
snprintf(buffer, 64, "LON: %f", (double)gps_uart->status.longitude);
28+
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, buffer);
29+
30+
release_mutex((ValueMutex*)context, gps_uart);
31+
}
32+
33+
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
34+
furi_assert(event_queue);
35+
36+
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
37+
furi_message_queue_put(event_queue, &event, FuriWaitForever);
38+
}
39+
40+
int32_t gps_app(void* p) {
41+
UNUSED(p);
42+
43+
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
44+
45+
GpsUart* gps_uart = gps_uart_enable();
46+
47+
ValueMutex gps_uart_mutex;
48+
if(!init_mutex(&gps_uart_mutex, gps_uart, sizeof(GpsUart))) {
49+
FURI_LOG_E("GPS", "cannot create mutex\r\n");
50+
free(gps_uart);
51+
return 255;
52+
}
53+
54+
// set system callbacks
55+
ViewPort* view_port = view_port_alloc();
56+
view_port_draw_callback_set(view_port, render_callback, &gps_uart_mutex);
57+
view_port_input_callback_set(view_port, input_callback, event_queue);
58+
59+
// open GUI and register view_port
60+
Gui* gui = furi_record_open("gui");
61+
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
62+
63+
PluginEvent event;
64+
for(bool processing = true; processing;) {
65+
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
66+
67+
GpsUart* gps_uart = (GpsUart*)acquire_mutex_block(&gps_uart_mutex);
68+
69+
if(event_status == FuriStatusOk) {
70+
// press events
71+
if(event.type == EventTypeKey) {
72+
if(event.input.type == InputTypePress) {
73+
switch(event.input.key) {
74+
case InputKeyUp:
75+
case InputKeyDown:
76+
case InputKeyRight:
77+
case InputKeyLeft:
78+
case InputKeyOk:
79+
break;
80+
case InputKeyBack:
81+
processing = false;
82+
break;
83+
}
84+
}
85+
}
86+
} else {
87+
FURI_LOG_D("GPS", "FuriMessageQueue: event timeout");
88+
}
89+
90+
view_port_update(view_port);
91+
release_mutex(&gps_uart_mutex, gps_uart);
92+
}
93+
94+
view_port_enabled_set(view_port, false);
95+
gui_remove_view_port(gui, view_port);
96+
furi_record_close("gui");
97+
view_port_free(view_port);
98+
furi_message_queue_free(event_queue);
99+
delete_mutex(&gps_uart_mutex);
100+
gps_uart_disable(gps_uart);
101+
102+
return 0;
103+
}

src/gps_uart.c

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include <string.h>
2+
3+
#include "minmea.h"
4+
#include "gps_uart.h"
5+
6+
typedef enum {
7+
WorkerEvtStop = (1 << 0),
8+
WorkerEvtRxDone = (1 << 1),
9+
} WorkerEvtFlags;
10+
11+
#define WORKER_ALL_RX_EVENTS \
12+
(WorkerEvtStop | WorkerEvtRxDone)
13+
14+
static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
15+
GpsUart* gps_uart = (GpsUart*)context;
16+
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
17+
18+
if(ev == UartIrqEventRXNE) {
19+
xStreamBufferSendFromISR(gps_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
20+
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtRxDone);
21+
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
22+
}
23+
}
24+
25+
static void gps_uart_serial_init(GpsUart* gps_uart) {
26+
furi_hal_console_disable();
27+
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, gps_uart_on_irq_cb, gps_uart);
28+
furi_hal_uart_set_br(FuriHalUartIdUSART1, GPS_BAUDRATE);
29+
}
30+
31+
static void gps_uart_serial_deinit(GpsUart* gps_uart) {
32+
UNUSED(gps_uart);
33+
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
34+
furi_hal_console_enable();
35+
}
36+
37+
static void gps_uart_parse_nmea(GpsUart* gps_uart, char* line)
38+
{
39+
switch(minmea_sentence_id(line, false)) {
40+
case MINMEA_SENTENCE_RMC: {
41+
struct minmea_sentence_rmc frame;
42+
if (minmea_parse_rmc(&frame, line)) {
43+
gps_uart->status.latitude = minmea_tocoord(&frame.latitude);
44+
gps_uart->status.longitude = minmea_tocoord(&frame.longitude);
45+
}
46+
} break;
47+
48+
default: break;
49+
}
50+
}
51+
52+
static int32_t gps_uart_worker(void* context) {
53+
GpsUart* gps_uart = (GpsUart*)context;
54+
55+
gps_uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE * 5, 1);
56+
size_t rx_offset = 0;
57+
58+
gps_uart_serial_init(gps_uart);
59+
60+
while(1) {
61+
uint32_t events =
62+
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
63+
furi_check((events & FuriFlagError) == 0);
64+
if(events & WorkerEvtStop) break;
65+
if(events & WorkerEvtRxDone) {
66+
size_t len = 0;
67+
do {
68+
len = xStreamBufferReceive(gps_uart->rx_stream, gps_uart->rx_buf + rx_offset,
69+
RX_BUF_SIZE - 1 - rx_offset, 0);
70+
if(len > 0) {
71+
rx_offset += len;
72+
gps_uart->rx_buf[rx_offset] = '\0';
73+
74+
char * line_current = (char *)gps_uart->rx_buf;
75+
while(1) {
76+
while (*line_current == '\0' && line_current < (char *)gps_uart->rx_buf + rx_offset - 1)
77+
line_current++;
78+
char * newline = strchr(line_current, '\n');
79+
if(newline) {
80+
*newline = '\0';
81+
gps_uart_parse_nmea(gps_uart, line_current);
82+
line_current = newline + 1;
83+
} else {
84+
if(line_current > (char *)gps_uart->rx_buf) {
85+
rx_offset = 0;
86+
while(*line_current) {
87+
gps_uart->rx_buf[rx_offset++] = *(line_current++);
88+
}
89+
}
90+
break;
91+
}
92+
}
93+
}
94+
} while(len > 0);
95+
}
96+
}
97+
98+
gps_uart_serial_deinit(gps_uart);
99+
100+
vStreamBufferDelete(gps_uart->rx_stream);
101+
102+
return 0;
103+
}
104+
105+
GpsUart* gps_uart_enable() {
106+
GpsUart* gps_uart = malloc(sizeof(GpsUart));
107+
108+
gps_uart->status.latitude = 0.0;
109+
gps_uart->status.longitude = 0.0;
110+
111+
gps_uart->thread = furi_thread_alloc();
112+
furi_thread_set_name(gps_uart->thread, "GpsUartWorker");
113+
furi_thread_set_stack_size(gps_uart->thread, 1024);
114+
furi_thread_set_context(gps_uart->thread, gps_uart);
115+
furi_thread_set_callback(gps_uart->thread, gps_uart_worker);
116+
117+
furi_thread_start(gps_uart->thread);
118+
return gps_uart;
119+
}
120+
121+
void gps_uart_disable(GpsUart* gps_uart) {
122+
furi_assert(gps_uart);
123+
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop);
124+
furi_thread_join(gps_uart->thread);
125+
furi_thread_free(gps_uart->thread);
126+
free(gps_uart);
127+
}

src/gps_uart.h

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <furi_hal.h>
4+
#include <stream_buffer.h>
5+
6+
#define GPS_BAUDRATE 9600
7+
#define RX_BUF_SIZE 1024
8+
9+
typedef struct {
10+
float latitude;
11+
float longitude;
12+
} GpsStatus;
13+
14+
typedef struct {
15+
FuriThread* thread;
16+
StreamBufferHandle_t rx_stream;
17+
uint8_t rx_buf[RX_BUF_SIZE];
18+
19+
GpsStatus status;
20+
} GpsUart;
21+
22+
GpsUart* gps_uart_enable();
23+
24+
void gps_uart_disable(GpsUart* gps_uart);

0 commit comments

Comments
 (0)