{"id":4301,"date":"2019-10-02T20:00:45","date_gmt":"2019-10-02T18:00:45","guid":{"rendered":"https:\/\/msalamon.pl\/?p=4301"},"modified":"2025-12-27T19:21:16","modified_gmt":"2025-12-27T18:21:16","slug":"gps-positioning-using-the-neo-6m-v2-on-an-stm32","status":"publish","type":"post","link":"https:\/\/msalamon.pl\/en\/gps-positioning-using-the-neo-6m-v2-on-an-stm32\/","title":{"rendered":"GPS positioning using the NEO-6M V2 on an STM32"},"content":{"rendered":"\n<p>Can you imagine life without navigation? I, for one, can\u2019t. Location devices are, for me, one of the best inventions. All the more so because we have them in our pockets. We can also add a GPS receiver to our electronic projects. See how simple it is!<\/p>\n\n\n\n<!--more-->\n\n\n\n<h1 class=\"wp-block-heading\">How does GPS work?<\/h1>\n\n\n\n<p>In a very brief nutshell. There are 31 satellites orbiting the Earth whose flight parameters are precisely known. They are at an altitude of about 20 thousand kilometers above the Earth\u2019s surface. The orbits of these satellites are circular, and it takes them exactly 11 hours and 58 minutes to orbit the planet. There are 28 satellites permanently active. The rest are test units or simply switched off.<\/p>\n\n\n\n<p>These satellites transmit signals on two frequencies: 1575.42 MHz and 1227.6 MHz. Based on the signals received from the satellites, the receiver is able to calculate our position.<\/p>\n\n\n\n<p>But how? For example, knowing the propagation speed of a radio wave, we can determine the time it takes for radio signals to reach the receiver. The transmitted signal contains several pieces of information useful for locating, including satellite positions, so on this basis we can determine our coordinates. The more satellites are visible, the greater the positioning accuracy.<\/p>\n\n\n\n<p><a href=\"https:\/\/www.youtube.com\/watch?v=YqjBbJdur_I\" target=\"_blank\" rel=\"noopener\">You can find a very good description of the entire system on the RS Elektronika YouTube channel<\/a>. I highly recommend taking a look there.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">NEO6MV2 GPS receiver<\/h2>\n\n\n\n<p>The manufacturer of the GPS module is <strong>u-blox<\/strong>. The NEO-6 receivers are a whole family of chips. The exact one I\u2019m dealing with today is the NEO-6-M-0-001, and the module it\u2019s mounted on is called <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO6MV2<\/a>.&nbsp;<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/produkt\/modul-gps-gy-neo6mv2-niebieski\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=neo6&amp;utm_content=neo6\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"400\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner.jpg\" alt=\"\" class=\"wp-image-1191\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-768x256.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-24x8.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-36x12.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/NEP6MV2_baner-160x53.jpg 160w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>The documentation lists a few interesting details about the receiver itself. The most important are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Maximum number of satellites: 50<\/li>\n\n\n\n<li>Cold start: 27 seconds<\/li>\n\n\n\n<li>Warm start: 27 seconds<\/li>\n\n\n\n<li>Hot start: 1 second<\/li>\n\n\n\n<li>Assisted start (A-GPS): under 3 seconds<\/li>\n\n\n\n<li>Positioning accuracy: 2.5 meters<\/li>\n\n\n\n<li>Maximum position update rate: 5 Hz (1 Hz by default)<\/li>\n\n\n\n<li>Interfaces: UART, USB, SPI<\/li>\n<\/ul>\n\n\n\n<p>The complete module has a status LED, EEPROM to store configuration, and a backup battery that helps with warm\/hot starts.<\/p>\n\n\n\n<p>Only the UART interface is brought out to the <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO6MV2<\/a> module header. It is the primary form of communication. The module also has a \u00b5FL connector for attaching an antenna. You can buy the module with an antenna included. It\u2019s usually a small antenna that works pretty well outdoors, but is definitely worse indoors. I had to wait a long time with the antenna stuck to the window.<\/p>\n\n\n\n<p>After powering up, the device immediately transmits NMEA messages on the RX pin. What is this enigmatic acronym?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">NMEA<\/h3>\n\n\n\n<p>NMEA (National Marine Electronics Association) is a communication protocol between marine electronic devices. Interesting, right? However, it is the standard when it comes to GPS devices.&nbsp;<\/p>\n\n\n\n<p>Data comes as ASCII-encoded messages. They are therefore somewhat human-readable. A single message can contain a maximum of 82 characters. Each sentence begins with a dollar sign ($), then there is the sentence identifier, followed by subsequent data separated by commas. The sentence ends with the character sequence \u201c\\n\\r\u201d.<\/p>\n\n\n\n<p>For the GPS system all messages start with \u201c$GP\u201d. Dozens of message types are defined, but for GPS I found 19 useful ones, which are described here: <a href=\"http:\/\/aprs.gids.nl\/nmea\/\" target=\"_blank\" rel=\"noopener\">http:\/\/aprs.gids.nl\/nmea\/<\/a><\/p>\n\n\n\n<p>In the default configuration, the NEO-6 outputs only a few of them:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>$GPRMC \u2013 recommended message, minimum navigation data set<\/li>\n\n\n\n<li>$GPVTG \u2013 course and speed<\/li>\n\n\n\n<li>$GPGGA \u2013 GPS navigation data determined in the receiver<\/li>\n\n\n\n<li>$GPGSA \u2013 DOP (dilution of precision) coefficients<\/li>\n\n\n\n<li>$GPGSV \u2013 PRN numbers and positions of potentially visible satellites and signal strength<\/li>\n\n\n\n<li>$GPGLL \u2013 geographic position<\/li>\n<\/ul>\n\n\n\n<p>Some data is repeated across several messages. Of course, you can edit what the receiver outputs on the UART interface. You can use the UBX protocol for this, but it\u2019s easier to connect the module to a computer and use the <strong>u-center<\/strong> application. It\u2019s a powerful tool that will, of course, read data from the <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO-6 module<\/a>, but also configure it freely, and even update the module\u2019s firmware. To connect a module that only has UART, you can use a <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/akcesoria\/konwertery\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">UART&lt;-&gt;USB converter<\/a>, which you can buy <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/akcesoria\/konwertery\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">in my store<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Connections and Cube configuration<\/h2>\n\n\n\n<p>Today I\u2019ll use the <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-f401re\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">Nucleo F401RE<\/a> board. For firmware configuration and programming I used STM32CubeIDE version 1.0.2 and the HAL libraries F4 1.24.1.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><figure><a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/dev-boardy\/stm32-nucleo\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=neo6&amp;utm_content=nucleo\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg\" alt=\"\" width=\"1200\" height=\"400\" class=\"aligncenter wp-image-1593 size-full\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-768x256.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\"><\/a><\/figure><\/h1>\n\n\n\n<p>Allow me, perhaps for the first time on the blog, to skip the schematic. Connecting the module is really simple and boils down to powering it and wiring the TX and RX pins to the Nucleo. I powered the GPS board from 5V, and connected communication to UART1:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>TX GPS to RX Nucleo on PA10<\/strong><\/li>\n\n\n\n<li><strong>RX GPS to TX Nucleo on PA9<\/strong><\/li>\n<\/ul>\n\n\n\n<p>I also brought out a test pin for timing measurements \ud83d\ude42<\/p>\n\n\n\n<p>Now Cube. In addition to the standard Nucleo settings such as HCLK = 84 MHz, set UART1 to <strong>9600 baud 8n1<\/strong>. Also enable the <strong>global interrupt for UART1<\/strong> in the NVIC Settings tab.<\/p>\n\n\n\n<p>You can now generate the project \ud83d\ude42 After generation, it\u2019s very important to add <em>-u_printf_floa<\/em>t to the linker options because we\u2019re going to use it.&nbsp;<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"288\" height=\"300\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker-288x300.jpg\" alt=\"\" class=\"wp-image-1178\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker-288x300.jpg 288w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker-768x800.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker-24x24.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker-77x80.jpg 77w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_linker.jpg 804w\" sizes=\"auto, (max-width: 288px) 100vw, 288px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code<\/h2>\n\n\n\n<p>I haven\u2019t written anything based on UART reception for a long time. But I managed hehe \ud83d\ude42 This time I opted for receiving single characters in an interrupt so as not to blockingly wait for incoming data. Step by step.<\/p>\n\n\n\n<p>First, we should initialize the library. I created a struct variable to store all the data related to our module. Thanks to this structure, you can connect several independent <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO-6 modules<\/a> because it contains a pointer to the UART and its own receive and working buffer for it.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">typedef struct\n{\n\t\/\/\n\t\/\/ UART stuff\n\t\/\/\n\tUART_HandleTypeDef *neo6_huart;\n\tuint8_t UartBuffer[GPS_UART_BUFFER_SIZE];\n\tuint8_t UartBufferHead;\n\tuint8_t UartBufferTail;\n\tuint8_t UartBufferLines;\n\n\tuint8_t WorkingBuffer[GPS_WORKING_BUFFER_SIZE];\n\n\t\/\/\n\t\/\/ Time and Date\n\t\/\/\n\tuint8_t Hour;\n\tuint8_t Minute;\n\tuint8_t Second;\n\tuint8_t Day;\n\tuint8_t Month;\n\tuint8_t Year;\n\n\t\/\/\n\t\/\/ Position\n\t\/\/\n\tdouble Latitude;\n\tchar LatitudeDirection;\n\tdouble Longitude;\n\tchar LongitudeDirection;\n\tdouble Altitude;\n\n\t\/\/\n\t\/\/ Speed\n\t\/\/\n\tdouble SpeedKnots;\n\tdouble SpeedKilometers;\n\n\t\/\/\n\t\/\/ Satelites parameters\n\t\/\/\n\tuint8_t SatelitesNumber;\n\tuint8_t Quality; \/\/ 0 - no Fix, 1 - Fix, 2 - Dif. Fix\n\tuint8_t FixMode; \/\/ 1 - no Fiz, 2 - 2D, 3 - 3D\n\tdouble Dop; \/\/ Dilution of precision\n\tdouble Hdop; \/\/ Dilution of precision for flat coords\n\tdouble Vdop; \/\/ Dilution of precision for height\n\n}NEO6_State;<\/pre>\n\n\n\n<p>Initialization therefore fills the structure with initial data, assigns the UART for the receiver, and starts listening on the serial interface.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void NEO6_Init(NEO6_State *GpsStateHandler, UART_HandleTypeDef *huart);<\/pre>\n\n\n\n<p>Let me discuss how the STM32 acquires all the data from the GPS receiver. As you remember, the <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO-6<\/a> sends data by itself, once per second by default. The UART interrupt is set for every single received byte. Why? We\u2019re never sure how many characters will arrive. Besides, this way it\u2019s easier to control the moment when the end of the message arrives by recognizing the \u2018\\n\u2019 character.<\/p>\n\n\n\n<p>Receiving a character is handled in the <em>NEO6_ReceiveUartChar<\/em> function, which should be placed in the UART receive-complete interrupt.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN 4 *\/\nvoid HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)\n{\n  if(huart == GpsState.neo6_huart)\n  {\n    NEO6_ReceiveUartChar(&amp;amp;GpsState);\n  }\n}\n\/* USER CODE END 4 *\/<\/pre>\n\n\n\n<p>Let\u2019s look inside this function. It operates on the receive buffer from the GPS data structure. This is a circular\/ring buffer with no beginning or end. New data is appended at the next position in the buffer, and consumed data is removed from the \u201cstart\u201d. Meanwhile, that start and end keep moving depending on what is being done on the buffer.<\/p>\n\n\n\n<p>As overflow handling, I chose to drop incoming data. Fortunately, by setting a sufficiently large buffer, and as long as the microcontroller isn\u2019t blocked on some task, such choking shouldn\u2019t occur. It\u2019s good to set the buffer to a value of 2x the longest message. As we know from the NMEA standard, it will be 81 characters at most, so the minimum buffer is 162 bytes. I set 256 because it\u2019s such a nice, round number.<\/p>\n\n\n\n<p>Okay. What happens when there is space? Three conditions are checked:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Character 0x0D, i.e. &lt;CR> or \u2018\\n\u2019. Do you remember what\u2019s at the end of an NMEA sentence? Among other things, this character. When it\u2019s received, I increment the variable that tells how many messages are waiting to be analyzed and, of course, write this character to the ring buffer.<\/li>\n\n\n\n<li>Characters 0x0A and 0x00. 0x0A is &lt;LF> or \u2018\\r\u2019. One terminating character is enough, so I drop this one. Same with zero. It carries no information in ASCII communication. On the other hand, it also shouldn\u2019t happen that we get a zero from the module, so it could be ignored in considerations\u2026<\/li>\n\n\n\n<li>The remaining characters are simply added to the buffer.<\/li>\n<\/ol>\n\n\n\n<p>At the very end I re-enable UART RX listening for one character.<\/p>\n\n\n\n<p>There is a function called&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void NEO6_Task(NEO6_State *GpsStateHandler);<\/pre>\n\n\n\n<p>which you must place in the program\u2019s main loop. It checks whether a complete message has already arrived. If so, it fetches the whole thing from the ring buffer into the working buffer and analyzes (parses) the message. How can such a message be parsed?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Parsing NMEA<\/h3>\n\n\n\n<p>Two (three) functions are very useful for this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>strcmp<\/strong> \u2013 comparing C-strings<\/li>\n\n\n\n<li><strong>strtok<\/strong> \u2013 splitting a string into smaller chunks by a \u201ctoken\u201d<\/li>\n\n\n\n<li>strtoke \u2013 a modification of strtok I\u2019ll get to in a moment<\/li>\n<\/ul>\n\n\n\n<p>Notice one characteristic thing in NMEA. Data is always separated by a comma and each piece of information has its defined place. An example of what the module prints after getting a fix:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"689\" height=\"366\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data.jpg\" alt=\"\" class=\"wp-image-1179\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data.jpg 689w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data-300x159.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data-36x19.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_raw_data-151x80.jpg 151w\" sizes=\"auto, (max-width: 689px) 100vw, 689px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>There\u2019s also a small piece after the asterisk which is a checksum. I\u2019ll skip it during parsing. I trust I\u2019m receiving correct data.<\/p>\n\n\n\n<p>It would be good to leverage those commas. For this, the <em>strtok<\/em> function is perfect.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">char *strtok(char *s, const char *delim);<\/pre>\n\n\n\n<p>How do you use it? It takes as arguments a pointer to the string to be split and the <em>token<\/em> by which it will split. Our token is a comma.<\/p>\n\n\n\n<p>What does it do with that comma? It inserts 0x00 in its place, i.e. the end-of-C-string character, and returns a pointer to this modified string. Now you can easily operate on it.<\/p>\n\n\n\n<p>Alright, but did the rest of the buffer just disappear or what? What about the characters after the comma?!<\/p>\n\n\n\n<p>Well, the next call to <em>strtok<\/em> will return a pointer to the next string, i.e. the one after the last found token, meaning the characters between the first and second comma. It\u2019s important that you don\u2019t pass a pointer to the next string as the first argument now. To continue splitting the same buffer, you should now pass NULL as the first argument.<\/p>\n\n\n\n<p>An example straight from the code. To extract the NMEA message header I use <em>strtok<\/em>. Let\u2019s say the message looks like this:<\/p>\n\n\n\n<p class=\"has-text-align-center\">$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">char* ParsePoiner = strtok((char*)GpsStateHandler-&amp;gt;WorkingBuffer, \",\");<\/pre>\n\n\n\n<p>I passed a pointer to the working buffer which contains the full NMEA message. As the token, of course, \u2018,\u2019. Under <strong>ParsePointer<\/strong> we now have the string \u201c$GPRMC\u201d.&nbsp;<\/p>\n\n\n\n<p>Next call<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ParsePoiner = strtok(NULL, \",\");<\/pre>\n\n\n\n<p>Will result in <strong>ParsePointer<\/strong> now being the string \u201c081836\u201d, representing the time. We can now convert it to a number and assign it to appropriate variables.<\/p>\n\n\n\n<p>Let\u2019s call <em>strtok<\/em> once again.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ParsePoiner = strtok(NULL, \",\");<\/pre>\n\n\n\n<p>Now <strong>ParsePointer<\/strong> is \u201cA\u201d. We keep doing this until we obtain all the data, operating on those split strings with functions that convert ASCII to float, for example. And that\u2019s the whole magic of parsing this way. I hope you get it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The strtok trap<\/h2>\n\n\n\n<p>There\u2019s no rose without thorns. A big thorn is this super function <em>strtok<\/em> itself. Why? Notice that some data fields are empty, i.e. there are two commas in a row. Especially if there is no fix. Why is this dangerous?<\/p>\n\n\n\n<p>Well, <strong>the <em>strtok<\/em> function does not return empty strings.<\/strong> Instead, it returns the next string with some content. As a result, if some data is missing in the sequence of tokens, our parsing gets broken\u2026 You can write some smarter parsing logic, but\u2026 you can also write a better token function. One that will return an empty string.<\/p>\n\n\n\n<p>I found an implementation of such a function on <a href=\"https:\/\/stackoverflow.com\/questions\/42315585\/split-string-into-tokens-in-c-when-there-are-2-delimiters-in-a-row\" target=\"_blank\" rel=\"noopener\">the site loved by programmers with problems<\/a>.<\/p>\n\n\n\n<p>The function is called <em><strong>strtoke<\/strong><\/em> and works exactly like standard <em>strok<\/em> except that it can return an empty string. You can safely replace all calls with this improved one. Now empty fields won\u2019t be skipped. Success!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Result<\/h2>\n\n\n\n<p>The sample code I wrote prints to UART2 the data it received from the GPS on UART1. However, it doesn\u2019t always print\u2014only when the module has a fix.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"781\" height=\"598\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix.jpg\" alt=\"\" class=\"wp-image-1181\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix.jpg 781w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix-300x230.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix-768x588.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix-24x18.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix-36x28.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_no_fix-104x80.jpg 104w\" sizes=\"auto, (max-width: 781px) 100vw, 781px\" \/><\/a><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"781\" height=\"598\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data.jpg\" alt=\"\" class=\"wp-image-1180\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data.jpg 781w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data-300x230.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data-768x588.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data-24x18.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data-36x28.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_fix_data-104x80.jpg 104w\" sizes=\"auto, (max-width: 781px) 100vw, 781px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p> Let\u2019s also look at the logic analyzer to see how the interrupts and data parsing are doing.<\/p>\n\n\n\n<p>Receiving all messages from the<a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\"> NEO-6<\/a> looks like this.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"115\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-1024x115.jpg\" alt=\"\" class=\"wp-image-1182\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-1024x115.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-300x34.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-768x86.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-1536x173.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-24x3.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-36x4.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data-160x18.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_receiving_data.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>The entire frame the module sends is over 100 ms. This one doesn\u2019t have a fix. It won\u2019t lock on my desk\u2026 Adding data after getting a GPS signal, this frame will be a bit longer.<\/p>\n\n\n\n<p>As you can see in the second row there are lots of interrupt calls for character reception. They are triggered often so they look serious. How much do they knock the microcontroller out of its normal operation?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"113\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-1024x113.jpg\" alt=\"\" class=\"wp-image-1183\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-1024x113.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-300x33.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-768x85.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-1536x170.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-24x3.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-36x4.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time-160x18.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_interrupt_time.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>The interrupt lasts about 2.8 \u00b5s. Considering that this time also includes handling the test GPIO pin, that\u2019s very little. Such an interrupt is invoked every exactly 1.04 ms when the next character arrives.<\/p>\n\n\n\n<p>On an even lower level you can see a few spikes. I marked the moments of data parsing here.&nbsp;<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"119\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-1024x119.jpg\" alt=\"\" class=\"wp-image-1184\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-1024x119.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-300x35.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-768x90.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-1536x179.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-24x3.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-36x4.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time-160x19.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/09\/neo6mv2_parse_time.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Parsing, i.e. splitting, searching strings, and converting data takes about 100 \u00b5s.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>Handling GPS is very easy. Just a few tricks are enough to fully enjoy the messages transmitted by the <a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/moduly\/czujniki-atmosferyczne\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=neo6&amp;utm_content=Text\">NEO-6 module<\/a>. Reception and data processing are very fast. Also remember that the times I presented relate to an HCLK frequency of 84 MHz.<\/p>\n\n\n\n<p>Where can you use such a GPS? Lately I see an application for it in a measurement device with LoRa communication. We kind of know where the device is, but when producing several of them, we don\u2019t have to worry about manually entering the location. A cool solution was recently suggested to me by one of the readers. It was a measurement drone. It performs air quality measurements and automatically saves them to an SD card together with the measurement location. Great use case.<\/p>\n\n\n\n<p>An additional advantage of using GPS is the accurate time. You can use it to synchronize the RTC or even consider removing the clock from the device altogether.<\/p>\n\n\n\n<p>GPS receivers are everywhere these days. For example in smartphones or cars. Even drones have GPS, which, for example, assists in piloting the machine.<\/p>\n\n\n\n<p>You can find the full project along with the library on my GitHub as usual: <a href=\"https:\/\/github.com\/lamik\/GPS_NEO6_STM32_HAL\" target=\"_blank\" rel=\"noopener\">link<\/a><\/p>\n\n\n\n<p><span>If you noticed any mistake, disagree with something, would like to add something important or simply feel like discussing the topic, leave a comment. Remember that the discussion should be polite and in accordance with the rules of the Polish language.<\/span><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;4301&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;0&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 - (0 votes)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;title&quot;:&quot;GPS positioning using the NEO-6M V2 on an STM32&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Can you imagine life without navigation? I, for one, can\u2019t. Location devices are, for me, one of the best inventions. All the more so because we have them in our pockets. We can also add a GPS receiver to our electronic projects. See how simple it is!<\/p>\n","protected":false},"author":1,"featured_media":3303,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[160],"tags":[175,176,174,177],"class_list":["post-4301","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stm32","tag-electronics","tag-programming","tag-stm32","tag-stm32cubemx"],"_links":{"self":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/comments?post=4301"}],"version-history":[{"count":3,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4301\/revisions"}],"predecessor-version":[{"id":4410,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4301\/revisions\/4410"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media\/3303"}],"wp:attachment":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media?parent=4301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/categories?post=4301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/tags?post=4301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}