Skip to content

Commit

Permalink
Import and FAP conversion of GPS plugin from flipperzero-firmware for…
Browse files Browse the repository at this point in the history
…k. Does not build yet as stream_buffer.h API is not yet available for FAP.
  • Loading branch information
ezod committed Oct 2, 2022
1 parent 2feee6c commit fe98f61
Show file tree
Hide file tree
Showing 6 changed files with 1,240 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/application.fam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
App(
appid="gps",
name="GPS",
apptype=FlipperAppType.EXTERNAL,
entry_point="gps_app",
cdefines=["APP_GPS"],
requires=[
"gui",
],
stack_size=1 * 1024,
order=20,
)
103 changes: 103 additions & 0 deletions src/gps_app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include "gps_uart.h"

#include <furi.h>
#include <gui/gui.h>
#include <string.h>

typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;

typedef struct {
EventType type;
InputEvent input;
} PluginEvent;

static void render_callback(Canvas* const canvas, void* context) {
const GpsUart* gps_uart = acquire_mutex((ValueMutex*)context, 25);
if(gps_uart == NULL) {
return;
}

canvas_set_font(canvas, FontSecondary);
char buffer[64];
snprintf(buffer, 64, "LAT: %f", (double)gps_uart->status.latitude);
canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, buffer);
snprintf(buffer, 64, "LON: %f", (double)gps_uart->status.longitude);
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, buffer);

release_mutex((ValueMutex*)context, gps_uart);
}

static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);

PluginEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}

int32_t gps_app(void* p) {
UNUSED(p);

FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));

GpsUart* gps_uart = gps_uart_enable();

ValueMutex gps_uart_mutex;
if(!init_mutex(&gps_uart_mutex, gps_uart, sizeof(GpsUart))) {
FURI_LOG_E("GPS", "cannot create mutex\r\n");
free(gps_uart);
return 255;
}

// set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &gps_uart_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);

// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);

PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);

GpsUart* gps_uart = (GpsUart*)acquire_mutex_block(&gps_uart_mutex);

if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
case InputKeyOk:
break;
case InputKeyBack:
processing = false;
break;
}
}
}
} else {
FURI_LOG_D("GPS", "FuriMessageQueue: event timeout");
}

view_port_update(view_port);
release_mutex(&gps_uart_mutex, gps_uart);
}

view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&gps_uart_mutex);
gps_uart_disable(gps_uart);

return 0;
}
127 changes: 127 additions & 0 deletions src/gps_uart.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include <string.h>

#include "minmea.h"
#include "gps_uart.h"

typedef enum {
WorkerEvtStop = (1 << 0),
WorkerEvtRxDone = (1 << 1),
} WorkerEvtFlags;

#define WORKER_ALL_RX_EVENTS \
(WorkerEvtStop | WorkerEvtRxDone)

static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
GpsUart* gps_uart = (GpsUart*)context;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

if(ev == UartIrqEventRXNE) {
xStreamBufferSendFromISR(gps_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtRxDone);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}

static void gps_uart_serial_init(GpsUart* gps_uart) {
furi_hal_console_disable();
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, gps_uart_on_irq_cb, gps_uart);
furi_hal_uart_set_br(FuriHalUartIdUSART1, GPS_BAUDRATE);
}

static void gps_uart_serial_deinit(GpsUart* gps_uart) {
UNUSED(gps_uart);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
furi_hal_console_enable();
}

static void gps_uart_parse_nmea(GpsUart* gps_uart, char* line)
{
switch(minmea_sentence_id(line, false)) {
case MINMEA_SENTENCE_RMC: {
struct minmea_sentence_rmc frame;
if (minmea_parse_rmc(&frame, line)) {
gps_uart->status.latitude = minmea_tocoord(&frame.latitude);
gps_uart->status.longitude = minmea_tocoord(&frame.longitude);
}
} break;

default: break;
}
}

static int32_t gps_uart_worker(void* context) {
GpsUart* gps_uart = (GpsUart*)context;

gps_uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE * 5, 1);
size_t rx_offset = 0;

gps_uart_serial_init(gps_uart);

while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check((events & FuriFlagError) == 0);
if(events & WorkerEvtStop) break;
if(events & WorkerEvtRxDone) {
size_t len = 0;
do {
len = xStreamBufferReceive(gps_uart->rx_stream, gps_uart->rx_buf + rx_offset,
RX_BUF_SIZE - 1 - rx_offset, 0);
if(len > 0) {
rx_offset += len;
gps_uart->rx_buf[rx_offset] = '\0';

char * line_current = (char *)gps_uart->rx_buf;
while(1) {
while (*line_current == '\0' && line_current < (char *)gps_uart->rx_buf + rx_offset - 1)
line_current++;
char * newline = strchr(line_current, '\n');
if(newline) {
*newline = '\0';
gps_uart_parse_nmea(gps_uart, line_current);
line_current = newline + 1;
} else {
if(line_current > (char *)gps_uart->rx_buf) {
rx_offset = 0;
while(*line_current) {
gps_uart->rx_buf[rx_offset++] = *(line_current++);
}
}
break;
}
}
}
} while(len > 0);
}
}

gps_uart_serial_deinit(gps_uart);

vStreamBufferDelete(gps_uart->rx_stream);

return 0;
}

GpsUart* gps_uart_enable() {
GpsUart* gps_uart = malloc(sizeof(GpsUart));

gps_uart->status.latitude = 0.0;
gps_uart->status.longitude = 0.0;

gps_uart->thread = furi_thread_alloc();
furi_thread_set_name(gps_uart->thread, "GpsUartWorker");
furi_thread_set_stack_size(gps_uart->thread, 1024);
furi_thread_set_context(gps_uart->thread, gps_uart);
furi_thread_set_callback(gps_uart->thread, gps_uart_worker);

furi_thread_start(gps_uart->thread);
return gps_uart;
}

void gps_uart_disable(GpsUart* gps_uart) {
furi_assert(gps_uart);
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop);
furi_thread_join(gps_uart->thread);
furi_thread_free(gps_uart->thread);
free(gps_uart);
}
24 changes: 24 additions & 0 deletions src/gps_uart.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <furi_hal.h>
#include <stream_buffer.h>

#define GPS_BAUDRATE 9600
#define RX_BUF_SIZE 1024

typedef struct {
float latitude;
float longitude;
} GpsStatus;

typedef struct {
FuriThread* thread;
StreamBufferHandle_t rx_stream;
uint8_t rx_buf[RX_BUF_SIZE];

GpsStatus status;
} GpsUart;

GpsUart* gps_uart_enable();

void gps_uart_disable(GpsUart* gps_uart);
Loading

0 comments on commit fe98f61

Please sign in to comment.