This is a tutorial on how to use the NEO-6M GPS module with the Raspberry Pi Pico board. The NEO-6M GPS module is a stand-alone GPS module that uses the MediaTek MTK3339 chipset, a no-nonsense, high-quality GPS module that can track up to 22 satellites. This module is very easy to use with the Raspberry Pi Pico.
Required Material
- Raspberry Pi Pico
- NEO-6M GPS module
- Jumper wires
- Power supply
- Breadboard
NEO-6M GPS module
The NEO-6M GPS module is a small electronic module that uses satellite signals to determine accurate geographical coordinates, making it the most suitable choice for tracking and navigation projects. It can communicate with microcontrollers like the Raspberry Pi Pico through UART (Universal Asynchronous Receiver/Transmitter) protocol, allowing for easy integration.
It receives signals from orbiting satellites and uses them to determine your latitude and longitude coordinates. this module can also generate other useful information like the current time, date, and the speed at which you travel.
Specifications
- Power Supply Range: 3.3 V to 5 V
- Model: GY-GPS6MV2
- Ceramic antenna
- EEPROM for saving the configuration data when powered off
- Backup battery
- LED signal indicator
- Mounting Hole Diameter: 3 mm
- Default Baud Rate: 9600 bps
- Module size 23mm * 30mm
- Antenna size 12 * 12mm
- Cable:20mm
Pinout of NEO-6M GPS module
- VCC: Connect to a 3.3V power source.
- GND: Connect to ground (GND).
- TX: Transmit data
- RX: Receive data
Make sure to connect TX and RX pins correctly between the GPS module and microcontroller for bidirectional communication.
Interfacing NEO-6M GPS Module to Raspberry Pi Pico
The NEO-6M GPS module is a tiny electronic device that helps your Raspberry Pi Pico detect its geographical location.
You need to build the mapping between the GPS and Raspberry Pi Pico using jumper wires. Make the following connections:
- GPS module VCC to Raspberry Pi Pico 3.3V pin
- GPS module GND to Raspberry Pi Pico GND pin
- GPS module TX to Raspberry Pi Pico GP5 pin
- GPS module RX to Raspberry Pi Pico GP4 pin
By wiring the NEO-6M GPS module to your Raspberry Pi Pico and adopting some programming, you can develop projects dealing with tracking your movement or creating navigation means.
Micropython Code & Libraries for NEO-6M GPS Module
The code is separated into two parts: one for the GPS library For Micropython uses and another for the main code.
- Connect the Raspberry Pi Pico to your computer using a USB cable.
- Make sure you have installed Thonny IDE or Getting Started With Raspberry Pi Pico With Thonny IDE
micropyGPS.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 |
""" # MicropyGPS - a GPS NMEA sentence parser for Micropython/Python 3.X # Copyright (c) 2017 Michael Calvin McCoy ([email protected]) # The MIT License (MIT) - see LICENSE file """ # TODO: # Time Since First Fix # Distance/Time to Target # More Helper Functions # Dynamically limit sentences types to parse from math import floor, modf # Import utime or time for fix time handling try: # Assume running on MicroPython import utime except ImportError: # Otherwise default to time module for non-embedded implementations # Should still support millisecond resolution. import time class MicropyGPS(object): """GPS NMEA Sentence Parser. Creates object that stores all relevant GPS data and statistics. Parses sentences one character at a time using update(). """ # Max Number of Characters a valid sentence can be (based on GGA sentence) SENTENCE_LIMIT = 90 __HEMISPHERES = ('N', 'S', 'E', 'W') __NO_FIX = 1 __FIX_2D = 2 __FIX_3D = 3 __DIRECTIONS = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW') __MONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December') def __init__(self, local_offset=0, location_formatting='ddm'): """ Setup GPS Object Status Flags, Internal Data Registers, etc local_offset (int): Timzone Difference to UTC location_formatting (str): Style For Presenting Longitude/Latitude: Decimal Degree Minute (ddm) - 40° 26.767′ N Degrees Minutes Seconds (dms) - 40° 26′ 46″ N Decimal Degrees (dd) - 40.446° N """ ##################### # Object Status Flags self.sentence_active = False self.active_segment = 0 self.process_crc = False self.gps_segments = [] self.crc_xor = 0 self.char_count = 0 self.fix_time = 0 ##################### # Sentence Statistics self.crc_fails = 0 self.clean_sentences = 0 self.parsed_sentences = 0 ##################### # Logging Related self.log_handle = None self.log_en = False ##################### # Data From Sentences # Time self.timestamp = [0, 0, 0.0] self.date = [0, 0, 0] self.local_offset = local_offset # Position/Motion self._latitude = [0, 0.0, 'N'] self._longitude = [0, 0.0, 'W'] self.coord_format = location_formatting self.speed = [0.0, 0.0, 0.0] self.course = 0.0 self.altitude = 0.0 self.geoid_height = 0.0 # GPS Info self.satellites_in_view = 0 self.satellites_in_use = 0 self.satellites_used = [] self.last_sv_sentence = 0 self.total_sv_sentences = 0 self.satellite_data = dict() self.hdop = 0.0 self.pdop = 0.0 self.vdop = 0.0 self.valid = False self.fix_stat = 0 self.fix_type = 1 ######################################## # Coordinates Translation Functions ######################################## @property def latitude(self): """Format Latitude Data Correctly""" if self.coord_format == 'dd': decimal_degrees = self._latitude[0] + (self._latitude[1] / 60) return [decimal_degrees, self._latitude[2]] elif self.coord_format == 'dms': minute_parts = modf(self._latitude[1]) seconds = round(minute_parts[0] * 60) return [self._latitude[0], int(minute_parts[1]), seconds, self._latitude[2]] else: return self._latitude @property def longitude(self): """Format Longitude Data Correctly""" if self.coord_format == 'dd': decimal_degrees = self._longitude[0] + (self._longitude[1] / 60) return [decimal_degrees, self._longitude[2]] elif self.coord_format == 'dms': minute_parts = modf(self._longitude[1]) seconds = round(minute_parts[0] * 60) return [self._longitude[0], int(minute_parts[1]), seconds, self._longitude[2]] else: return self._longitude ######################################## # Logging Related Functions ######################################## def start_logging(self, target_file, mode="append"): """ Create GPS data log object """ # Set Write Mode Overwrite or Append mode_code = 'w' if mode == 'new' else 'a' try: self.log_handle = open(target_file, mode_code) except AttributeError: print("Invalid FileName") return False self.log_en = True return True def stop_logging(self): """ Closes the log file handler and disables further logging """ try: self.log_handle.close() except AttributeError: print("Invalid Handle") return False self.log_en = False return True def write_log(self, log_string): """Attempts to write the last valid NMEA sentence character to the active file handler """ try: self.log_handle.write(log_string) except TypeError: return False return True ######################################## # Sentence Parsers ######################################## def gprmc(self): """Parse Recommended Minimum Specific GPS/Transit data (RMC)Sentence. Updates UTC timestamp, latitude, longitude, Course, Speed, Date, and fix status """ # UTC Timestamp try: utc_string = self.gps_segments[1] if utc_string: # Possible timestamp found hours = (int(utc_string[0:2]) + self.local_offset) % 24 minutes = int(utc_string[2:4]) seconds = float(utc_string[4:]) self.timestamp = [hours, minutes, seconds] else: # No Time stamp yet self.timestamp = [0, 0, 0.0] except ValueError: # Bad Timestamp value present return False # Date stamp try: date_string = self.gps_segments[9] # Date string printer function assumes to be year >=2000, # date_string() must be supplied with the correct century argument to display correctly if date_string: # Possible date stamp found day = int(date_string[0:2]) month = int(date_string[2:4]) year = int(date_string[4:6]) self.date = (day, month, year) else: # No Date stamp yet self.date = (0, 0, 0) except ValueError: # Bad Date stamp value present return False # Check Receiver Data Valid Flag if self.gps_segments[2] == 'A': # Data from Receiver is Valid/Has Fix # Longitude / Latitude try: # Latitude l_string = self.gps_segments[3] lat_degs = int(l_string[0:2]) lat_mins = float(l_string[2:]) lat_hemi = self.gps_segments[4] # Longitude l_string = self.gps_segments[5] lon_degs = int(l_string[0:3]) lon_mins = float(l_string[3:]) lon_hemi = self.gps_segments[6] except ValueError: return False if lat_hemi not in self.__HEMISPHERES: return False if lon_hemi not in self.__HEMISPHERES: return False # Speed try: spd_knt = float(self.gps_segments[7]) except ValueError: return False # Course try: if self.gps_segments[8]: course = float(self.gps_segments[8]) else: course = 0.0 except ValueError: return False # TODO - Add Magnetic Variation # Update Object Data self._latitude = [lat_degs, lat_mins, lat_hemi] self._longitude = [lon_degs, lon_mins, lon_hemi] # Include mph and hm/h self.speed = [spd_knt, spd_knt * 1.151, spd_knt * 1.852] self.course = course self.valid = True # Update Last Fix Time self.new_fix_time() else: # Clear Position Data if Sentence is 'Invalid' self._latitude = [0, 0.0, 'N'] self._longitude = [0, 0.0, 'W'] self.speed = [0.0, 0.0, 0.0] self.course = 0.0 self.valid = False return True def gpgll(self): """Parse Geographic Latitude and Longitude (GLL)Sentence. Updates UTC timestamp, latitude, longitude, and fix status""" # UTC Timestamp try: utc_string = self.gps_segments[5] if utc_string: # Possible timestamp found hours = (int(utc_string[0:2]) + self.local_offset) % 24 minutes = int(utc_string[2:4]) seconds = float(utc_string[4:]) self.timestamp = [hours, minutes, seconds] else: # No Time stamp yet self.timestamp = [0, 0, 0.0] except ValueError: # Bad Timestamp value present return False # Check Receiver Data Valid Flag if self.gps_segments[6] == 'A': # Data from Receiver is Valid/Has Fix # Longitude / Latitude try: # Latitude l_string = self.gps_segments[1] lat_degs = int(l_string[0:2]) lat_mins = float(l_string[2:]) lat_hemi = self.gps_segments[2] # Longitude l_string = self.gps_segments[3] lon_degs = int(l_string[0:3]) lon_mins = float(l_string[3:]) lon_hemi = self.gps_segments[4] except ValueError: return False if lat_hemi not in self.__HEMISPHERES: return False if lon_hemi not in self.__HEMISPHERES: return False # Update Object Data self._latitude = [lat_degs, lat_mins, lat_hemi] self._longitude = [lon_degs, lon_mins, lon_hemi] self.valid = True # Update Last Fix Time self.new_fix_time() else: # Clear Position Data if Sentence is 'Invalid' self._latitude = [0, 0.0, 'N'] self._longitude = [0, 0.0, 'W'] self.valid = False return True def gpvtg(self): """Parse Track Made Good and Ground Speed (VTG) Sentence. Updates speed and course""" try: course = float(self.gps_segments[1]) if self.gps_segments[1] else 0.0 spd_knt = float(self.gps_segments[5]) if self.gps_segments[5] else 0.0 except ValueError: return False # Include mph and km/h self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852) self.course = course return True def gpgga(self): """Parse Global Positioning System Fix Data (GGA) Sentence. Updates UTC timestamp, latitude, longitude, fix status, satellites in use, Horizontal Dilution of Precision (HDOP), altitude, geoid height and fix status""" try: # UTC Timestamp utc_string = self.gps_segments[1] # Skip timestamp if receiver doesn't have on yet if utc_string: hours = (int(utc_string[0:2]) + self.local_offset) % 24 minutes = int(utc_string[2:4]) seconds = float(utc_string[4:]) else: hours = 0 minutes = 0 seconds = 0.0 # Number of Satellites in Use satellites_in_use = int(self.gps_segments[7]) # Get Fix Status fix_stat = int(self.gps_segments[6]) except (ValueError, IndexError): return False try: # Horizontal Dilution of Precision hdop = float(self.gps_segments[8]) except (ValueError, IndexError): hdop = 0.0 # Process Location and Speed Data if Fix is GOOD if fix_stat: # Longitude / Latitude try: # Latitude l_string = self.gps_segments[2] lat_degs = int(l_string[0:2]) lat_mins = float(l_string[2:]) lat_hemi = self.gps_segments[3] # Longitude l_string = self.gps_segments[4] lon_degs = int(l_string[0:3]) lon_mins = float(l_string[3:]) lon_hemi = self.gps_segments[5] except ValueError: return False if lat_hemi not in self.__HEMISPHERES: return False if lon_hemi not in self.__HEMISPHERES: return False # Altitude / Height Above Geoid try: altitude = float(self.gps_segments[9]) geoid_height = float(self.gps_segments[11]) except ValueError: altitude = 0 geoid_height = 0 # Update Object Data self._latitude = [lat_degs, lat_mins, lat_hemi] self._longitude = [lon_degs, lon_mins, lon_hemi] self.altitude = altitude self.geoid_height = geoid_height # Update Object Data self.timestamp = [hours, minutes, seconds] self.satellites_in_use = satellites_in_use self.hdop = hdop self.fix_stat = fix_stat # If Fix is GOOD, update fix timestamp if fix_stat: self.new_fix_time() return True def gpgsa(self): """Parse GNSS DOP and Active Satellites (GSA) sentence. Updates GPS fix type, list of satellites used in fix calculation, Position Dilution of Precision (PDOP), Horizontal Dilution of Precision (HDOP), Vertical Dilution of Precision, and fix status""" # Fix Type (None,2D or 3D) try: fix_type = int(self.gps_segments[2]) except ValueError: return False # Read All (up to 12) Available PRN Satellite Numbers sats_used = [] for sats in range(12): sat_number_str = self.gps_segments[3 + sats] if sat_number_str: try: sat_number = int(sat_number_str) sats_used.append(sat_number) except ValueError: return False else: break # PDOP,HDOP,VDOP try: pdop = float(self.gps_segments[15]) hdop = float(self.gps_segments[16]) vdop = float(self.gps_segments[17]) except ValueError: return False # Update Object Data self.fix_type = fix_type # If Fix is GOOD, update fix timestamp if fix_type > self.__NO_FIX: self.new_fix_time() self.satellites_used = sats_used self.hdop = hdop self.vdop = vdop self.pdop = pdop return True def gpgsv(self): """Parse Satellites in View (GSV) sentence. Updates number of SV Sentences,the number of the last SV sentence parsed, and data on each satellite present in the sentence""" try: num_sv_sentences = int(self.gps_segments[1]) current_sv_sentence = int(self.gps_segments[2]) sats_in_view = int(self.gps_segments[3]) except ValueError: return False # Create a blank dict to store all the satellite data from this sentence in: # satellite PRN is key, tuple containing telemetry is value satellite_dict = dict() # Calculate Number of Satelites to pull data for and thus how many segment positions to read if num_sv_sentences == current_sv_sentence: # Last sentence may have 1-4 satellites; 5 - 20 positions sat_segment_limit = (sats_in_view - ((num_sv_sentences - 1) * 4)) * 5 else: sat_segment_limit = 20 # Non-last sentences have 4 satellites and thus read up to position 20 # Try to recover data for up to 4 satellites in sentence for sats in range(4, sat_segment_limit, 4): # If a PRN is present, grab satellite data if self.gps_segments[sats]: try: sat_id = int(self.gps_segments[sats]) except (ValueError,IndexError): return False try: # elevation can be null (no value) when not tracking elevation = int(self.gps_segments[sats+1]) except (ValueError,IndexError): elevation = None try: # azimuth can be null (no value) when not tracking azimuth = int(self.gps_segments[sats+2]) except (ValueError,IndexError): azimuth = None try: # SNR can be null (no value) when not tracking snr = int(self.gps_segments[sats+3]) except (ValueError,IndexError): snr = None # If no PRN is found, then the sentence has no more satellites to read else: break # Add Satellite Data to Sentence Dict satellite_dict[sat_id] = (elevation, azimuth, snr) # Update Object Data self.total_sv_sentences = num_sv_sentences self.last_sv_sentence = current_sv_sentence self.satellites_in_view = sats_in_view # For a new set of sentences, we either clear out the existing sat data or # update it as additional SV sentences are parsed if current_sv_sentence == 1: self.satellite_data = satellite_dict else: self.satellite_data.update(satellite_dict) return True ########################################## # Data Stream Handler Functions ########################################## def new_sentence(self): """Adjust Object Flags in Preparation for a New Sentence""" self.gps_segments = [''] self.active_segment = 0 self.crc_xor = 0 self.sentence_active = True self.process_crc = True self.char_count = 0 def update(self, new_char): """Process a new input char and updates GPS object if necessary based on special characters ('$', ',', '*') Function builds a list of received string that are validate by CRC prior to parsing by the appropriate sentence function. Returns sentence type on successful parse, None otherwise""" valid_sentence = False # Validate new_char is a printable char ascii_char = ord(new_char) if 10 <= ascii_char <= 126: self.char_count += 1 # Write Character to log file if enabled if self.log_en: self.write_log(new_char) # Check if a new string is starting ($) if new_char == '$': self.new_sentence() return None elif self.sentence_active: # Check if sentence is ending (*) if new_char == '*': self.process_crc = False self.active_segment += 1 self.gps_segments.append('') return None # Check if a section is ended (,), Create a new substring to feed # characters to elif new_char == ',': self.active_segment += 1 self.gps_segments.append('') # Store All Other printable character and check CRC when ready else: self.gps_segments[self.active_segment] += new_char # When CRC input is disabled, sentence is nearly complete if not self.process_crc: if len(self.gps_segments[self.active_segment]) == 2: try: final_crc = int(self.gps_segments[self.active_segment], 16) if self.crc_xor == final_crc: valid_sentence = True else: self.crc_fails += 1 except ValueError: pass # CRC Value was deformed and could not have been correct # Update CRC if self.process_crc: self.crc_xor ^= ascii_char # If a Valid Sentence Was received and it's a supported sentence, then parse it!! if valid_sentence: self.clean_sentences += 1 # Increment clean sentences received self.sentence_active = False # Clear Active Processing Flag if self.gps_segments[0] in self.supported_sentences: # parse the Sentence Based on the message type, return True if parse is clean if self.supported_sentences[self.gps_segments[0]](self): # Let host know that the GPS object was updated by returning parsed sentence type self.parsed_sentences += 1 return self.gps_segments[0] # Check that the sentence buffer isn't filling up with Garage waiting for the sentence to complete if self.char_count > self.SENTENCE_LIMIT: self.sentence_active = False # Tell Host no new sentence was parsed return None def new_fix_time(self): """Updates a high resolution counter with current time when fix is updated. Currently only triggered from GGA, GSA and RMC sentences""" try: self.fix_time = utime.ticks_ms() except NameError: self.fix_time = time.time() ######################################### # User Helper Functions # These functions make working with the GPS object data easier ######################################### def satellite_data_updated(self): """ Checks if the all the GSV sentences in a group have been read, making satellite data complete :return: boolean """ if self.total_sv_sentences > 0 and self.total_sv_sentences == self.last_sv_sentence: return True else: return False def unset_satellite_data_updated(self): """ Mark GSV sentences as read indicating the data has been used and future updates are fresh """ self.last_sv_sentence = 0 def satellites_visible(self): """ Returns a list of of the satellite PRNs currently visible to the receiver :return: list """ return list(self.satellite_data.keys()) def time_since_fix(self): """Returns number of millisecond since the last sentence with a valid fix was parsed. Returns 0 if no fix has been found""" # Test if a Fix has been found if self.fix_time == 0: return -1 # Try calculating fix time using utime; if not running MicroPython # time.time() returns a floating point value in secs try: current = utime.ticks_diff(utime.ticks_ms(), self.fix_time) except NameError: current = (time.time() - self.fix_time) * 1000 # ms return current def compass_direction(self): """ Determine a cardinal or inter-cardinal direction based on current course. :return: string """ # Calculate the offset for a rotated compass if self.course >= 348.75: offset_course = 360 - self.course else: offset_course = self.course + 11.25 # Each compass point is separated by 22.5 degrees, divide to find lookup value dir_index = floor(offset_course / 22.5) final_dir = self.__DIRECTIONS[dir_index] return final_dir def latitude_string(self): """ Create a readable string of the current latitude data :return: string """ if self.coord_format == 'dd': formatted_latitude = self.latitude lat_string = str(formatted_latitude[0]) + '° ' + str(self._latitude[2]) elif self.coord_format == 'dms': formatted_latitude = self.latitude lat_string = str(formatted_latitude[0]) + '° ' + str(formatted_latitude[1]) + "' " + str(formatted_latitude[2]) + '" ' + str(formatted_latitude[3]) else: lat_string = str(self._latitude[0]) + '° ' + str(self._latitude[1]) + "' " + str(self._latitude[2]) return lat_string def longitude_string(self): """ Create a readable string of the current longitude data :return: string """ if self.coord_format == 'dd': formatted_longitude = self.longitude lon_string = str(formatted_longitude[0]) + '° ' + str(self._longitude[2]) elif self.coord_format == 'dms': formatted_longitude = self.longitude lon_string = str(formatted_longitude[0]) + '° ' + str(formatted_longitude[1]) + "' " + str(formatted_longitude[2]) + '" ' + str(formatted_longitude[3]) else: lon_string = str(self._longitude[0]) + '° ' + str(self._longitude[1]) + "' " + str(self._longitude[2]) return lon_string def speed_string(self, unit='kph'): """ Creates a readable string of the current speed data in one of three units :param unit: string of 'kph','mph, or 'knot' :return: """ if unit == 'mph': speed_string = str(self.speed[1]) + ' mph' elif unit == 'knot': if self.speed[0] == 1: unit_str = ' knot' else: unit_str = ' knots' speed_string = str(self.speed[0]) + unit_str else: speed_string = str(self.speed[2]) + ' km/h' return speed_string def date_string(self, formatting='s_mdy', century='20'): """ Creates a readable string of the current date. Can select between long format: Januray 1st, 2014 or two short formats: 11/01/2014 (MM/DD/YYYY) 01/11/2014 (DD/MM/YYYY) :param formatting: string 's_mdy', 's_dmy', or 'long' :param century: int delineating the century the GPS data is from (19 for 19XX, 20 for 20XX) :return: date_string string with long or short format date """ # Long Format Januray 1st, 2014 if formatting == 'long': # Retrieve Month string from private set month = self.__MONTHS[self.date[1] - 1] # Determine Date Suffix if self.date[0] in (1, 21, 31): suffix = 'st' elif self.date[0] in (2, 22): suffix = 'nd' elif self.date[0] == (3, 23): suffix = 'rd' else: suffix = 'th' day = str(self.date[0]) + suffix # Create Day String year = century + str(self.date[2]) # Create Year String date_string = month + ' ' + day + ', ' + year # Put it all together else: # Add leading zeros to day string if necessary if self.date[0] < 10: day = '0' + str(self.date[0]) else: day = str(self.date[0]) # Add leading zeros to month string if necessary if self.date[1] < 10: month = '0' + str(self.date[1]) else: month = str(self.date[1]) # Add leading zeros to year string if necessary if self.date[2] < 10: year = '0' + str(self.date[2]) else: year = str(self.date[2]) # Build final string based on desired formatting if formatting == 's_dmy': date_string = day + '/' + month + '/' + year else: # Default date format date_string = month + '/' + day + '/' + year return date_string # All the currently supported NMEA sentences supported_sentences = {'GPRMC': gprmc, 'GLRMC': gprmc, 'GPGGA': gpgga, 'GLGGA': gpgga, 'GPVTG': gpvtg, 'GLVTG': gpvtg, 'GPGSA': gpgsa, 'GLGSA': gpgsa, 'GPGSV': gpgsv, 'GLGSV': gpgsv, 'GPGLL': gpgll, 'GLGLL': gpgll, 'GNGGA': gpgga, 'GNRMC': gprmc, 'GNVTG': gpvtg, 'GNGLL': gpgll, 'GNGSA': gpgsa, } if __name__ == "__main__": pass |
Simply copy the code and save it micropyGPS.py
on your Raspberry Pi Pico.
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from machine import Pin, UART import utime from micropyGPS import MicropyGPS # Initialize GPS module gps_module = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5)) time_zone = -3 gps = MicropyGPS(time_zone) def convert_coordinates(sections): if sections[0] == 0: # sections[0] contains the degrees return None # sections[1] contains the minutes data = sections[0] + (sections[1] / 60.0) # sections[2] contains 'E', 'W', 'N', 'S' if sections[2] == 'S': data = -data if sections[2] == 'W': data = -data data = '{0:.6f}'.format(data) # 6 decimal places return str(data) while True: length = gps_module.any() if length > 0: data = gps_module.read(length) for byte in data: message = gps.update(chr(byte)) latitude = convert_coordinates(gps.latitude) longitude = convert_coordinates(gps.longitude) if latitude is None or longitude is None: continue print('Lat: ' + latitude) print('Lon: ' + longitude) utime.sleep_ms(100) # Add a 100ms delay between GPS updates |
Click “Run” or “Upload” to transfer the code to the Raspberry Pi Pico.
To monitor the output:
- Check the console or output window of your programming environment.
- If the connections and UART settings are correct, you’ll see GPS data being printed.
Check location using latitude and longitude
Here’s an example of how you can use the latitude and longitude values obtained from the code and search for the location on www.latlong.net
- Open a web browser and go to www.latlong.net.
- Enter the latitude value in the “Latitude” input field on the website.
- Enter the longitude value in the “Longitude” input field on the website.
- Click on the “Convert” button on the website.
- The website will display the corresponding location on the map.
2 Comments
Check the diagram. You use 5 and 6 pins. The correct are 6 and 7. Regards
Thank you for pointing that out. I’ll make sure to use pins 6 and 7 as indicated in the diagram.