NEO-6M GPS Module with ESP32 using MicroPython

In this tutorial, we will learn about a NEO-6M GPS module and how to interface it with ESP32 using MicroPython to obtain GPS parameters such as latitude, longitude, altitude, date, time, speed, satellites, etc. We will learn how GPS works and the overview of the NEO-6M GPS module with an introduction, pinout, and specifications. After that, we will learn to interface a NEO-6M GPS Module module with ESP32.

NEO-6M GPS Module with ESP32 using MicroPython

For demonstration purposes, we will display the GPS location parameters including latitude, longitude, number of satellites, and current time on the Thonny shell console as well as on an OLED.

We have a similar guide with Raspberry Pi Pico:

Prerequisites

Before we start this lesson make sure you are familiar with and have the latest version of MicroPython firmware installed in your ESP32 board and have a running Integrated Development Environment (IDE) in which we will be doing the programming such as uPyCraft IDE or Thonny IDE.

If you want to use VS Code, you can follow this guide:

GPS Introduction

Recommended Reading: GPS ( Global Positioning System) – working

The Global Positioning System (GPS) is a satellite-based navigation system that consists of 24 orbiting satellites, each of which makes two circuits around the Earth every 24 hours. These satellites transmit three bits of information – the satellite’s number, its position in space, and the time the information is sent. These signals are picked up by the GPS receiver, which uses this information to calculate the distance between it and the GPS satellites. With signals from three or more satellites, a GPS receiver can triangulate its location on the ground (i.e., longitude and latitude) from the known position of the satellites. With four or more satellites, a GPS receiver can determine a 3D position (i.e., latitude, longitude, and elevation).

In addition, a GPS receiver can provide data on your speed and direction of travel. Anyone with a GPS receiver can access the system. Because GPS provides real-time, three-dimensional positioning, navigation, and timing 24 hours a day, 7 days a week, all over the world, it is used in numerous applications, including GIS data collection, surveying, and mapping.

Point to Remember: A GPS receiver locates any three or more of the satellites, calculates the distanc­e to each, and uses this information to generate its own location. This operation is based on a simple mathematical principle called Trilateration.

NEO-6M GPS Module Introduction

The NEO-6M GPS module is a GPS receiver that can locate all locations on Earth as it is able to track approximately 22 satellites. It consists of a high-performance u-blox 6 positioning engine. Measuring 16 x 12.2 x 2.4 mm, its compact architecture along with its low power consumption makes it a good choice for IoT projects. Overall it is a good cost-effective GPS receiver.

Hardware Overview

Let us learn a little bit about its hardware. To obtain GPS readings, we have to use the NEO-6M GPS module with an antenna. The antenna is firmly attached to the module via the U.FL connector. This connector is found on the GPS module.

NEO-6M GPS Module hardware overview

NEO-6M GPS Chip

In the middle of the GPS module, you can find the NEO-6M chip. This is responsible for tracking up to 22 satellites and any location on the Earth on several channels. Due to its highly sensitive tracking nature, it makes the NEO-6M module a popular GPS tracker.

Some key features of NEO-6M chip include:

  • High sensitivity for tracking
  • Low supply current (~45mA)
  • Is able to track 5 locations per second with an accuracy of 2.5m (horizontal).
  • Comes equipped with PSM also known as Power Saving Mode. This mode causes very less power consumption by turning the module ON/OFF according to the need.
  • Great use as GPS trackers in smart watches due to very low power consumption (~11mA)

Position Fix LED Indicator

Moving ahead, the module comes with a position fix LED indicator. This LED indicates through its blinking effect whether the module is searching for satellites or has already found them. If the LED blinks after every second, then it indicates that the position fix is found. However, if the LED does not blink then the module is still searching for the satellites.

3.3V low-dropout Regulator

The module also comes equipped with a 3.3V LDO regulator (MIC5205). This provides an efficient linear voltage regulation with ultralow-noise output and very low dropout voltage. Additionally, the module is can also tolerate 5V easily.

Specifications

The table below shows some specifications of the NEO-6M module.

TypeGPS
Supply2.7 V-3.6 V
Operating Current45mA
Operating Temperature-40°C ~ 85°C
Horizontal Position Accuracy2.5m
Communication ProtocolNMEA, UBX Binary, RTCM
FeaturesRTC Crystal and External Interrupt/Wake up
InterfaceUART, SPI, USB and DDC

For more information regarding the NEO-6M module refer to its datasheet given here.

Pinout of NEO 6M Module

The diagram below shows the pinout of the NEO 6M module. It consists of 4 pins named GND, TX, RX, and VCC.

NEO 6M GPS module pin out
GNDThis is the ground pin that will be connected with the ground of the microcontroller.
TXThis is the transmission pin used for serial communication.
RXThis is the receiver pin used for serial communication.
VCCThis is the VCC pin used to power up the GPS module.

Interfacing NEO-6M Module with ESP32 and OLED

ESP32 with NEO-6M and OLED hardware

We will need the following components to connect our ESP32 development board with the OLED Display and NEO-6M GPS module.

  1. ESP32 development board
  2. NEO-6M GPS Module
  3. SSD1306 OLED Display
  4. Connecting Wires

ESP32 with NEO-6M and OLED

The OLED display has 4 terminals which we will connect with the ESP32 board. As the OLED display requires an operating voltage in the range of 3.3-5V hence we will connect the VCC terminal with 3.3V which will be in common with the ESP32 board. SCL of the display will be connected with the SCL pin of the module and the SDA of the display will be connected with the SDA of the module. By default, the I2C pin in ESP32 for SDA is GPIO21, and for SCL is GPIO22. The connections between the two devices can be seen in the table below.

SSD1306 OLED DisplayESP32
VCC3.3V
SDAGPIO21(I2C SDA)
SCLGPIO22(I2C SCL)
GNDGND

Similarly, the NEO-6M GPS module also has 4 terminals which we will connect with the ESP32 board. As the GPS module requires an operating voltage in the range of 2.7-3.6V hence we will connect the VCC terminal with 3.3V which will be in common with the ESP32 board. The TX (transmitter) terminal of the GPS module will be connected with the RX2 pin of the ESP32 for communication. Likewise, the RX (receiver) terminal of the GPS module will be connected with the TX2 pin of the ESP32.

ESP32NEO-6M Module
3.3VVCC
RX2TX
TX2RX
GNDGND

All three devices will be commonly grounded.

The diagram below shows the schematic diagram of ESP32 with OLED display and NEO 6M Module.

ESP32 with NEO 6M and OLED schematic
ESP32 with NEO-6M and OLED
ESP32 with NEO-6M and OLED

Understanding basic NMEA sentence

The NEO-6M GPS module sends GPS data in NMEA format. It is of various kinds including $GPRMC, $GPGGA etc. Each NMEA data field is separated by a comma and starts with a ‘$’.

Below you can view the $GPXXX syntax that denotes the different types of NMEA messages:

$GPGGAGlobal Positioning System Fix Data. It provides 3D location and accuracy data
$GPGSAIt provides GPS DOP and active satellites
$GPGSVIt provides the detailed information of the GPS satellite
$GPGLLIt provides the geographic Latitude and Longitude
$GPRMCIt provides the position, velocity and time
$GPVTGIt provides the dual ground/water speed

Let us understand how to read the basic NMEA sentence i.e. $GPGGA. Here is an example $GPGGA NMEA sentence:

$GPGGA, 103005, 3807.038, N, 07128.99030, E, 1, 07, 1.43, 134.5, M, 42.9, M, , *78
  • $: This indicates the start of the NMEA sentence
  • GPGGA : Global Positioning System Fix Data
  • 103005 : This is the UTC time when the data was accessed in HH:MM:SS. In this case the time is 10:30:05 UTC
  • 3807.038, N : Latitude 38 degrees 07.038′ N
  • 07128.99030, E : Longitude 71 degrees 28.00030′ E
  • 1 : GPS fix
  • 07 : Number of satellites being tracked
  • 1.43 : Horizontal dilution of position
  • 134.5, M : This shows the altitude (m) above the sea level
  • 42.9, M : Height of geoid (mean sea level)
  • Empty field : time in seconds since last DGPS update
  • Empty field : DGPS station ID number
  • *78 : the checksum data

SSD1306 OLED MicroPython Library

We will have to install the SSD1306 OLED library for MicroPython to continue with our project.

  • To successfully do that, open your Thonny IDE with your ESP32 or ESP8266 plugged in your system. Go to Tools > Manage Packages. This will open up the Thonny Package Manager.
Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 1
  • Search for “ssd1306” in the search bar by typing its name and clicking the button ‘Search on PyPI.’
Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 2
  • From the following search results click on the one highlighted below: micropython-ssd1306
Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 3

Install this library.

Raspberry Pi Pico Installing ssd1306 OLED library MicoPython 4

After a few moments this library will get successfully installed. Now we are ready to program our ESP32 with OLED display.

MicroPython Script Obtaining GPS Data from NEO-6M Module

from machine import Pin, UART, SoftI2C
from ssd1306 import SSD1306_I2C

import utime, time

i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=10000)     #initializing the I2C method for ESP32

oled = SSD1306_I2C(128, 64, i2c)

gpsModule = UART(2, baudrate=9600)
print(gpsModule)

buff = bytearray(255)

TIMEOUT = False
FIX_STATUS = False

latitude = ""
longitude = ""
satellites = ""
GPStime = ""

def getGPS(gpsModule):
    global FIX_STATUS, TIMEOUT, latitude, longitude, satellites, GPStime
    
    timeout = time.time() + 8 
    while True:
        gpsModule.readline()
        buff = str(gpsModule.readline())
        parts = buff.split(',')
    
        if (parts[0] == "b'$GPGGA" and len(parts) == 15):
            if(parts[1] and parts[2] and parts[3] and parts[4] and parts[5] and parts[6] and parts[7]):
                print(buff)
                
                latitude = convertToDegree(parts[2])
                if (parts[3] == 'S'):
                    latitude = -latitude
                longitude = convertToDegree(parts[4])
                if (parts[5] == 'W'):
                    longitude = -longitude
                satellites = parts[7]
                GPStime = parts[1][0:2] + ":" + parts[1][2:4] + ":" + parts[1][4:6]
                FIX_STATUS = True
                break
                
        if (time.time() > timeout):
            TIMEOUT = True
            break
        utime.sleep_ms(500)
        
def convertToDegree(RawDegrees):

    RawAsFloat = float(RawDegrees)
    firstdigits = int(RawAsFloat/100) 
    nexttwodigits = RawAsFloat - float(firstdigits*100) 
    
    Converted = float(firstdigits + nexttwodigits/60.0)
    Converted = '{0:.6f}'.format(Converted) 
    return str(Converted)
    
    
while True:
    
    getGPS(gpsModule)

    if(FIX_STATUS == True):
        print("Printing GPS data...")
        print(" ")
        print("Latitude: "+latitude)
        print("Longitude: "+longitude)
        print("Satellites: " +satellites)
        print("Time: "+GPStime)
        print("----------------------")
        
        oled.fill(0)
        oled.text("Lat: "+latitude, 0, 0)
        oled.text("Lng: "+longitude, 0, 10)
        oled.text("Satellites: "+satellites, 0, 20)
        oled.text("Time: "+GPStime, 0, 30)
        oled.show()
        
        FIX_STATUS = False
        
    if(TIMEOUT == True):
        print("No GPS data is found.")
        TIMEOUT = False

How the Code Works?

We will start by importing the UART, Pin and SoftI2C class from the machine module. Next we will also import utime and time module to incorporate delays. We will also import the ssd1306 which is the OLED display library that we installed earlier. This will help us in accessing all the functions defined inside it.

from machine import Pin, UART, SoftI2C
from ssd1306 import SSD1306_I2C
import utime, time

Initializing OLED

Next, we will initialize the I2C GPIO pins for SCL and SDA respectively.

We have created an I2C() method which takes in three parameters. The first parameter specifies the I2C GPIO pin of the board which is connected to the SCL line. The second parameter specifies the I2C GPIO pin of the board which is connected to the SDA line. The last parameter is the frequency connection of the OLED.

We are setting the SCL on GPIO22 and the SDA on GPIO21.

i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=10000) 

This SCL and SDA pin data gets saved in the object ‘i2c’ which will connect to the I2C bus and help in the communication between the two devices

Now, we will create an object ‘oled’ of SSD1306_I2C which uses the width, height, and the i2c object as parameters.

oled = SSD1306_I2C(128, 64, i2c)

Initialize UART Communication

Then we will define the NEO-6M UART connection by creating an object ‘gpsModule’. By using UART() we specified the UART channel as the first parameter and the baud rate as the second parameter. We are using UART2 in this case with baud rate 9600 for the uart communication.

Then we will print these NEO-6M module connection details in the Thonny shell console.

gpsModule = UART(2, baudrate=9600)
print(gpsModule)

Data Variables

Then we will create a byte array object called buff to store the NMEA sentences.

buff = bytearray(255)

The following variables will store the GPS parameters including Latitude, Longitude, Number of satellites and UTC time.

latitude = ""
longitude = ""
satellites = ""
GPStime = ""

The following function obtains the GPS coordinates. We are running a while loop to obtain the GPS data from the basic NMEA sentence $GPGGA. Latitude, Longitude, Satellites and time will be acquired from the NMEA sentence accordingly and saved in their respective variables.

def getGPS(gpsModule):
    global FIX_STATUS, TIMEOUT, latitude, longitude, satellites, GPStime
    
    timeout = time.time() + 8 
    while True:
        gpsModule.readline()
        buff = str(gpsModule.readline())
        parts = buff.split(',')
    
        if (parts[0] == "b'$GPGGA" and len(parts) == 15):
            if(parts[1] and parts[2] and parts[3] and parts[4] and parts[5] and parts[6] and parts[7]):
                print(buff)
                
                latitude = convertToDegree(parts[2])
                if (parts[3] == 'S'):
                    latitude = -latitude
                longitude = convertToDegree(parts[4])
                if (parts[5] == 'W'):
                    longitude = -longitude
                satellites = parts[7]
                GPStime = parts[1][0:2] + ":" + parts[1][2:4] + ":" + parts[1][4:6]
                FIX_STATUS = True
                break
                
        if (time.time() > timeout):
            TIMEOUT = True
            break
        utime.sleep_ms(500)

The following function is responsible to convert the raw longitude and latitude data to actual values.

def convertToDegree(RawDegrees):

    RawAsFloat = float(RawDegrees)
    firstdigits = int(RawAsFloat/100) 
    nexttwodigits = RawAsFloat - float(firstdigits*100) 
    
    Converted = float(firstdigits + nexttwodigits/60.0)
    Converted = '{0:.6f}'.format(Converted) 
    return str(Converted)

Inside the infinite loop we will call the getGPS(gpsModule) function first. If the GPS data is available then print it both on the Thonny shell console and the OLED display. If the GPS data is not acquired within the time set within the program, then “No GPS data is found” message is printed on the shell console instead.

while True:
    
    getGPS(gpsModule)

    if(FIX_STATUS == True):
        print("Printing GPS data...")
        print(" ")
        print("Latitude: "+latitude)
        print("Longitude: "+longitude)
        print("Satellites: " +satellites)
        print("Time: "+GPStime)
        print("----------------------")
        
        oled.fill(0)
        oled.text("Lat: "+latitude, 0, 0)
        oled.text("Lng: "+longitude, 0, 10)
        oled.text("Satellites: "+satellites, 0, 20)
        oled.text("Time: "+GPStime, 0, 30)
        oled.show()
        
        FIX_STATUS = False
        
    if(TIMEOUT == True):
        print("No GPS data is found.")
        TIMEOUT = False

Demo

After you have copied the following code onto a new file, click the ‘Save’ icon to save your program code on your PC.

After you have saved the code press the Run button to upload the code to your board. Before uploading code make sure the correct board is selected.

In the shell terminal of your IDE, you will be able to view the following GPS parameters:

ESP32 with NEO-6M and OLED project thonny shell terminal

Once the code is uploaded to ESP32, you will be able to view the GPS data on the OLED as well:

ESP32 with NEO-6M and OLED project demo

To read more GPS related articles, follow the links below:

3 thoughts on “NEO-6M GPS Module with ESP32 using MicroPython”

  1. Hello,

    Thank you for this tuto. At least for ESP8266, it may be useful do detach UART(0) from REPL, otherwise GPS module will send sentence to terminal, which would try to interpret this as micropython commands.

    import os
    os.dupterm(None, 1)

    Don’t forget to attach UART(0) when finished :
    import os, machine
    uart = machine.UART(0, 115200)
    os.dupterm(uart, 1)

    See docs.micropython.org.

    Reply
  2. Thank you for this interesting tutorial. I just came across with an issue due to my location. I am actually in Buenos Aires, Argentina and the script gives an error because the city is in the southern hemisphere an in the west of Greenwich meridian. The scrip requires to convert the coordinates from positive to negative and then it gives the following error:
    Traceback (most recent call last):
    File “”, line 65, in
    File “”, line 38, in getGPS
    TypeError: unsupported type for __neg__: ‘str’
    Best regards
    Gustavo

    Reply
    • (continue)
      A quick and dirty solution could be:

      to replace the line:
      latitude = -latitude
      by:
      latitude = str(-float(latitude))

      And replace:
      latitude = -latitude
      by:
      latitude = str(-float(latitude))
      Best regards

      Reply

Leave a Comment