From 4ff5d239725d5d666c28303877bc8e17a5a929de Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 17 Dec 2017 02:37:28 +0800 Subject: [PATCH] Initial version --- .gitignore | 6 ++ .gitmodules | 3 + LICENSE | 21 +++++ README.md | 29 +++++++ component.mk | 36 ++++++++ example/Makefile | 6 ++ example/main/component.mk | 0 example/main/nmea_example_main.c | 143 +++++++++++++++++++++++++++++++ libnmea | 1 + 9 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE create mode 100644 README.md create mode 100644 component.mk create mode 100644 example/Makefile create mode 100644 example/main/component.mk create mode 100644 example/main/nmea_example_main.c create mode 160000 libnmea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2de4b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.settings +.cproject +.project +example/build +example/sdkconfig +.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a2df1fc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libnmea"] + path = libnmea + url = https://github.com/jacketizer/libnmea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8e4bbc8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jack Engqvist Johansson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..504ec47 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# NMEA parser component for ESP32 ESP-IDF + +This is a wrapper around [libnmea](https://github.com/jacketizer/libnmea), +in the form of an [ESP-IDF](https://github.com/espressif/esp-idf) component. + +To use, clone the component into the `components` directory of your project, +or add it as a submodule. +See [libnmea documentation](https://github.com/jacketizer/libnmea#how-to-use-it) +for more details. + + +## Example + +Example project is provided inside `example` directory. It works the same way +as `parse_stdin.c` example from libnmea, except that it reads NMEA sentences +from UART. Connect TXD pin of GPS receiver to GPIO21 of ESP32, build and +flash the example. Decoded NMEA messages will be displayed in the console. + +## Known issues + +`strptime` function in newlib is less smart than the one in Glibc, and can not +parse format strings such as '%H%M%S' (i.e. when there are no separators +between numeric fields). Therefore date and time fields of NMEA messages +are not parsed. + +## License + +[libnmea](https://github.com/jacketizer/libnmea), this component, and the +example project are licensed under MIT License. diff --git a/component.mk b/component.mk new file mode 100644 index 0000000..cb9efdd --- /dev/null +++ b/component.mk @@ -0,0 +1,36 @@ +COMPONENT_SUBMODULES := libnmea +COMPONENT_SRCDIRS := libnmea/src/nmea libnmea/src/parsers +COMPONENT_ADD_INCLUDEDIRS := libnmea/src/nmea libnmea/src/parsers +PARSER_OBJS := $(addprefix libnmea/src/parsers/,\ + gpgga.o \ + gpgll.o \ + gprmc.o \ + ) + +COMPONENT_OBJS := $(addprefix libnmea/src/,\ + nmea/nmea.o \ + nmea/parser_static.o \ + parsers/parse.o \ + ) \ + $(PARSER_OBJS) + +define RENAME_SYMBOLS + $(OBJCOPY) \ + --redefine-sym init=nmea_$(1)_init \ + --redefine-sym parse=nmea_$(1)_parse \ + --redefine-sym set_default=nmea_$(1)_set_default \ + --redefine-sym allocate_data=nmea_$(1)_allocate_data \ + --redefine-sym free_data=nmea_$(1)_free_data \ + libnmea/src/parsers/$(1).o +endef + +$(COMPONENT_LIBRARY): | rename_symbols + +.PHONY: rename_symbols + +rename_symbols: | $(PARSER_OBJS) + $(call RENAME_SYMBOLS,gpgga) + $(call RENAME_SYMBOLS,gpgll) + $(call RENAME_SYMBOLS,gprmc) + +CPPFLAGS += -DENABLE_GPGLL=1 -DENABLE_GPGGA=1 -DENABLE_GPRMC=1 -DPARSER_COUNT=3 diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..4efa916 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,6 @@ +PROJECT_NAME := nmea_example + +EXTRA_COMPONENT_DIRS := $(realpath ..) + +include $(IDF_PATH)/make/project.mk + diff --git a/example/main/component.mk b/example/main/component.mk new file mode 100644 index 0000000..e69de29 diff --git a/example/main/nmea_example_main.c b/example/main/nmea_example_main.c new file mode 100644 index 0000000..f7e766a --- /dev/null +++ b/example/main/nmea_example_main.c @@ -0,0 +1,143 @@ +/* NMEA parsing example for ESP32. + * Based on "parse_stdin.c" example from libnmea. + * Copyright (c) 2015 Jack Engqvist Johansson. + * Additions Copyright (c) 2017 Ivan Grokhotkov. + * See "LICENSE" file in libnmea directory for license. + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/uart.h" +#include "nmea.h" +#include "gpgll.h" +#include "gpgga.h" +#include "gprmc.h" + +#define UART_NUM UART_NUM_2 +#define UART_RX_PIN 21 +#define UART_RX_BUF_SIZE (1024) + +static void uart_setup(); +static void read_and_parse_nmea(); + +void app_main() +{ + uart_setup(); + read_and_parse_nmea(); +} + +static void uart_setup() +{ + uart_config_t uart_config = { + .baud_rate = 9600, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(UART_NUM, &uart_config); + uart_set_pin(UART_NUM, + UART_PIN_NO_CHANGE, 21, + UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(UART_NUM, UART_RX_BUF_SIZE * 2, 0, 0, NULL, 0); +} + + +static void read_and_parse_nmea() +{ + // Configure a temporary buffer for the incoming data + char *buffer = (char*) malloc(UART_RX_BUF_SIZE + 1); + char fmt_buf[32]; + size_t total_bytes = 0; + while (1) { + // Read data from the UART + int read_bytes = uart_read_bytes(UART_NUM, + (uint8_t*) buffer + total_bytes, + UART_RX_BUF_SIZE - total_bytes, 100 / portTICK_RATE_MS); + if (read_bytes <= 0) { + continue; + } + + nmea_s *data; + total_bytes += read_bytes; + + /* find start (a dollar sign) */ + char* start = memchr(buffer, '$', total_bytes); + if (start == NULL) { + total_bytes = 0; + continue; + } + + /* find end of line */ + char* end = memchr(start, '\r', total_bytes - (start - buffer)); + if (NULL == end || '\n' != *(++end)) { + continue; + } + end[-1] = NMEA_END_CHAR_1; + end[0] = NMEA_END_CHAR_2; + + /* handle data */ + data = nmea_parse(start, end - start + 1, 0); + if (data != NULL) { + if (data->errors != 0) { + printf("WARN: The sentence struct contains parse errors!\n"); + } + + if (NMEA_GPGGA == data->type) { + printf("GPGGA sentence\n"); + nmea_gpgga_s *gpgga = (nmea_gpgga_s *) data; + printf("Number of satellites: %d\n", gpgga->n_satellites); + printf("Altitude: %d %c\n", gpgga->altitude, + gpgga->altitude_unit); + } + + if (NMEA_GPGLL == data->type) { + printf("GPGLL sentence\n"); + nmea_gpgll_s *pos = (nmea_gpgll_s *) data; + printf("Longitude:\n"); + printf(" Degrees: %d\n", pos->longitude.degrees); + printf(" Minutes: %f\n", pos->longitude.minutes); + printf(" Cardinal: %c\n", (char) pos->longitude.cardinal); + printf("Latitude:\n"); + printf(" Degrees: %d\n", pos->latitude.degrees); + printf(" Minutes: %f\n", pos->latitude.minutes); + printf(" Cardinal: %c\n", (char) pos->latitude.cardinal); + strftime(fmt_buf, sizeof(fmt_buf), "%H:%M:%S", &pos->time); + printf("Time: %s\n", fmt_buf); + } + + if (NMEA_GPRMC == data->type) { + printf("GPRMC sentence\n"); + nmea_gprmc_s *pos = (nmea_gprmc_s *) data; + printf("Longitude:\n"); + printf(" Degrees: %d\n", pos->longitude.degrees); + printf(" Minutes: %f\n", pos->longitude.minutes); + printf(" Cardinal: %c\n", (char) pos->longitude.cardinal); + printf("Latitude:\n"); + printf(" Degrees: %d\n", pos->latitude.degrees); + printf(" Minutes: %f\n", pos->latitude.minutes); + printf(" Cardinal: %c\n", (char) pos->latitude.cardinal); + strftime(fmt_buf, sizeof(fmt_buf), "%H:%M:%S", &pos->time); + printf("Time: %s\n", fmt_buf); + } + + nmea_free(data); + } + + /* buffer empty? */ + if (end == buffer + total_bytes) { + total_bytes = 0; + continue; + } + + /* copy rest of buffer to beginning */ + if (buffer != memmove(buffer, end, total_bytes - (end - buffer))) { + total_bytes = 0; + continue; + } + + total_bytes -= end - buffer; + } + free(buffer); +} diff --git a/libnmea b/libnmea new file mode 160000 index 0000000..527ba78 --- /dev/null +++ b/libnmea @@ -0,0 +1 @@ +Subproject commit 527ba78ccb4e4f4829ee1b793d9046c89bc557e2