Skip to content

Commit 65c523f

Browse files
yousefyasserSeifAbbasRasheed-Atia
committed
feat: add GPS UART module with NMEA parsing and checksum validation
Co-authored-by: NightsThunder <[email protected]> Co-authored-by: RasheedAtia <[email protected]>
1 parent b6ac07f commit 65c523f

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

uart/gps_uart/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Tell CMake where to find the executable source file
2+
add_executable(gps_uart gps_uart.c)
3+
4+
# Link to pico_stdlib (gpio, time, etc. functions)
5+
target_link_libraries(gps_uart pico_stdlib hardware_uart)
6+
7+
# Enable usb output, disable uart output
8+
pico_enable_stdio_usb(gps_uart 1)
9+
pico_enable_stdio_uart(gps_uart 1)
10+
11+
# Create map/bin/hex/uf2 files
12+
pico_add_extra_outputs(gps_uart)

uart/gps_uart/gps_uart.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#include "hardware/uart.h"
2+
#include "pico/stdlib.h"
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <stdbool.h>
8+
9+
10+
#define UART_ID uart1
11+
#define BAUD_RATE 9600
12+
#define UART_TX_PIN 4
13+
#define UART_RX_PIN 5
14+
#define MAX_NMEA_LENGTH 256
15+
16+
typedef struct
17+
{
18+
float latitude;
19+
float longitude;
20+
bool is_valid;
21+
} GPSData;
22+
23+
24+
void uart_gps_init()
25+
{
26+
uart_init(UART_ID, BAUD_RATE);
27+
28+
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
29+
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
30+
31+
uart_set_hw_flow(UART_ID, false, false);
32+
uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE);
33+
uart_set_fifo_enabled(UART_ID, true);
34+
}
35+
36+
bool validate_nmea_checksum(char *nmea_string)
37+
{
38+
int len = strlen(nmea_string);
39+
printf("Checksum Validation - String Length: %d\n", len);
40+
printf("Full NMEA String: %s", nmea_string);
41+
42+
if (len < 7)
43+
{
44+
printf("Invalid: Too short (< 7 chars)\n");
45+
return false;
46+
}
47+
48+
char *checksum_ptr = strchr(nmea_string, '*');
49+
if (!checksum_ptr)
50+
{
51+
printf("Invalid: No checksum marker (*) found\n");
52+
return false;
53+
}
54+
55+
uint8_t calculated_checksum = 0;
56+
for (char *p = nmea_string + 1; p < checksum_ptr; p++)
57+
{
58+
calculated_checksum ^= *p;
59+
}
60+
61+
char hex_checksum[3];
62+
snprintf(hex_checksum, sizeof(hex_checksum), "%02X", calculated_checksum);
63+
64+
printf("Calculated Checksum: %s\n", hex_checksum);
65+
printf("Received Checksum: %s\n", checksum_ptr + 1);
66+
67+
bool is_valid = strncmp(hex_checksum, checksum_ptr + 1, 2) == 0;
68+
printf("Checksum Validation: %s\n", is_valid ? "VALID" : "INVALID");
69+
70+
return is_valid;
71+
}
72+
73+
void convert_nmea_to_decimal(char *nmea_coord, float *decimal_coord)
74+
{
75+
float degrees = atof(nmea_coord) / 100.0;
76+
int int_degrees = (int)degrees;
77+
float minutes = (degrees - int_degrees) * 100.0;
78+
79+
*decimal_coord = int_degrees + (minutes / 60.0);
80+
}
81+
82+
bool parse_nmea_gps(char *nmea_string, GPSData *gps_data)
83+
{
84+
if (!validate_nmea_checksum(nmea_string))
85+
{
86+
printf("Invalid NMEA Checksum\n");
87+
return false;
88+
}
89+
90+
// Ignore all NMEA strings that are not of type GPRMC
91+
if (strncmp(nmea_string, "$GPRMC", 6) != 0)
92+
{
93+
return false;
94+
}
95+
96+
char *token;
97+
char *tokens[12] = {0};
98+
int token_count = 0;
99+
100+
// Tokenize the string
101+
token = strtok(nmea_string, ",");
102+
while (token != NULL && token_count < 12)
103+
{
104+
tokens[token_count++] = token;
105+
106+
token = strtok(NULL, ",");
107+
}
108+
109+
// Check if we have enough tokens and data is valid
110+
if (token_count >= 12 && strcmp(tokens[2], "A") == 0)
111+
{
112+
if (strlen(tokens[3]) > 0)
113+
{
114+
convert_nmea_to_decimal(tokens[3], &gps_data->latitude);
115+
if (tokens[4][0] == 'S')
116+
gps_data->latitude = -gps_data->latitude;
117+
}
118+
119+
if (strlen(tokens[5]) > 0)
120+
{
121+
convert_nmea_to_decimal(tokens[5], &gps_data->longitude);
122+
if (tokens[6][0] == 'W')
123+
gps_data->longitude = -gps_data->longitude;
124+
}
125+
126+
gps_data->is_valid = true;
127+
return true;
128+
}
129+
}
130+
131+
void process_gps_data(GPSData *gps_data)
132+
{
133+
char nmea_buffer[MAX_NMEA_LENGTH];
134+
int chars_read = 0;
135+
136+
while (uart_is_readable(UART_ID) && chars_read < MAX_NMEA_LENGTH - 1)
137+
{
138+
char received_char = uart_getc(UART_ID);
139+
nmea_buffer[chars_read] = received_char;
140+
141+
// NMEA strings are terminated by a newline character
142+
if ((int)received_char == 10)
143+
{
144+
nmea_buffer[chars_read + 1] = '\0';
145+
146+
if (parse_nmea_gps(nmea_buffer, gps_data))
147+
{
148+
printf("Valid GPS Data Received\n");
149+
}
150+
151+
chars_read = 0;
152+
break;
153+
}
154+
else
155+
{
156+
chars_read++;
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)