ESP32 with Ublox NEO-6M

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
nikanta
Posts: 9
Joined: Sun Jun 16, 2019 10:28 am

ESP32 with Ublox NEO-6M

Post by nikanta » Sat Feb 15, 2020 9:34 pm

For the past days, I have been trying to make the ESP32 work with the Ublox NEO-6M GPS module.
I haven't found any libraries to work with micropython.
I have connected the gps module with the esp and it is blinking red every second which if i'm not wrong means that it has a gps lock.
I just need to get the gps latitude and longitude once in a while and not every second.
Is there any library or any code I can use?

User avatar
jimmo
Posts: 1465
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: ESP32 with Ublox NEO-6M

Post by jimmo » Sat Feb 15, 2020 11:41 pm

Most GPS devices communicate with the host by sending NMEA data over a UART. You should be able to read this using machine.UART on the ESP32.

NMEA is text based so it's easy to see whether you're getting data...my memory from debugging NMEA systems on boats is that it's something like $GPRMC is the line that has the lat/long in it.

There was a thread on the forum recently discussing using https://github.com/peterhinch/micropyth ... master/gps -- that looks like a great way to work with this.

nikanta
Posts: 9
Joined: Sun Jun 16, 2019 10:28 am

Re: ESP32 with Ublox NEO-6M

Post by nikanta » Sun Feb 16, 2020 6:57 pm

jimmo wrote:
Sat Feb 15, 2020 11:41 pm
Most GPS devices communicate with the host by sending NMEA data over a UART. You should be able to read this using machine.UART on the ESP32.

NMEA is text based so it's easy to see whether you're getting data...my memory from debugging NMEA systems on boats is that it's something like $GPRMC is the line that has the lat/long in it.

There was a thread on the forum recently discussing using https://github.com/peterhinch/micropyth ... master/gps -- that looks like a great way to work with this.
I saw the github link.
And yes it does work and very nicely indeed.
I am trying to get the NMEA data and then edit them myself.
This is my part of the code that has to do with gps

Code: Select all

from machine import UART

uart = UART(2, 9600)
uart.init(9600, bits=8, parity=None, stop=1) 
test = uart.read()
print(test)
if(test == None):
    print('No location found')
else:
    #edit the NMEA data
With the above code i did get the NMEA data some times but now all i get is None.
And if i run the script many times in a row i'll freeze the esp board.
Both the gps board and the esp board are at the same spot where the github code gets coordinates normally.
So i'm definitely doing something wrong here and therefore i'm not being able to get the NMEA data.

EDIT
I just pressed the EN button of the ESP.
When i ran the script again i get NMEA data.
But only for the first time i run it.
Then i get 'None' again as data.
So every time i press the EN button i get data for the first run of the script only.

wthie
Posts: 1
Joined: Sun Feb 16, 2020 9:30 pm

Re: ESP32 with Ublox NEO-6M

Post by wthie » Sun Feb 16, 2020 10:23 pm

Maybe you can glean from the test code below, running just fine on a TTGO T-22-V1.7 with a NEO-6M. MicropyGPS still needs some work as it's not really coded to deal with erroneous NMEA strings. As you can see the T22 board has an additional cheap OLED display of the SSD1306 family connected to GPIO0 and GPIO4 working as I2C to make the module a standalone GPS receiver.
If you only want to see the acquired data on the console uncomment the #print lines.

Hint: I usually work with Thonny as a minimal IDE for my microPython work, giving me really good turn around times when programming.

Links:
MicropyGPS - https://github.com/inmcm/micropyGPS
Thonny - https://thonny.org/

Code: Select all

import time
import machine
from micropyGPS import MicropyGPS
import ssd1306
import _thread
import time

WIDTH  = 128
HEIGHT = 64

def main():
    i2c = machine.I2C(scl=machine.Pin(0), sda=machine.Pin(4))
    dsp = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, 0x3c, False)

    uart = machine.UART(1, rx=12, tx=15, baudrate=9600, bits=8, parity=None, stop=1, timeout=5000, rxbuf=1024)

    gps = MicropyGPS()

    while True:
      buf = uart.readline()

      for char in buf:
        gps.update(chr(char))  # Note the conversion to to chr, UART outputs ints normally

      #print('UTC Timestamp:', gps.timestamp)
      #print('Date:', gps.date_string('long'))
      #print('Latitude:', gps.latitude)
      #print('Longitude:', gps.longitude_string())
      #print('Horizontal Dilution of Precision:', gps.hdop)
      #print('Altitude:', gps.altitude)
      #print('Satellites:', gps.satellites_in_use)
      #print()
      
      dsp.fill(0)
      y = 0
      dy = 10
      dsp.text("{}".format(gps.date_string('s_mdy')), 0, y)
      dsp.text("Sat:{}".format(gps.satellites_in_use), 80, y)
      y += dy
      dsp.text("{:02d}:{:02d}:{:02.0f}".format(gps.timestamp[0], gps.timestamp[1], gps.timestamp[2]),  0, y)
      y += dy
      dsp.text("Lat:{}{:3d}'{:02.4f}".format(gps.latitude[2], gps.latitude[0], gps.latitude[1]),  0, y)
      y += dy
      dsp.text("Lon:{}{:3d}'{:02.4f}".format(gps.longitude[2], gps.longitude[0], gps.longitude[1]),  0, y)
      y += dy
      dsp.text("Alt:{:0.0f}ft".format(gps.altitude * 1000 / (12*25.4)),  0, y)
      y += dy
      dsp.text("HDP:{:0.2f}".format(gps.hdop),  0, y)
      dsp.show()

 
def startGPSthread():
    _thread.start_new_thread(main, ())

if __name__ == "__main__":
  print('...running main, GPS testing')
  main()

User avatar
jimmo
Posts: 1465
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: ESP32 with Ublox NEO-6M

Post by jimmo » Sun Feb 16, 2020 11:28 pm

nikanta wrote:
Sun Feb 16, 2020 6:57 pm
Then i get 'None' again as data.
The GPS will only send data over the UART periodically, so not every call to read() will return data.

But you also need to do more work to buffer and re-assemble lines. I assume this is what micropygps and as_GPS do.

nikanta
Posts: 9
Joined: Sun Jun 16, 2019 10:28 am

Re: ESP32 with Ublox NEO-6M

Post by nikanta » Tue Feb 18, 2020 1:47 pm

wthie wrote:
Sun Feb 16, 2020 10:23 pm
Maybe you can glean from the test code below, running just fine on a TTGO T-22-V1.7 with a NEO-6M. MicropyGPS still needs some work as it's not really coded to deal with erroneous NMEA strings. As you can see the T22 board has an additional cheap OLED display of the SSD1306 family connected to GPIO0 and GPIO4 working as I2C to make the module a standalone GPS receiver.
If you only want to see the acquired data on the console uncomment the #print lines.

Hint: I usually work with Thonny as a minimal IDE for my microPython work, giving me really good turn around times when programming.

Links:
MicropyGPS - https://github.com/inmcm/micropyGPS
Thonny - https://thonny.org/

Code: Select all

import time
import machine
from micropyGPS import MicropyGPS
import ssd1306
import _thread
import time

WIDTH  = 128
HEIGHT = 64

def main():
    i2c = machine.I2C(scl=machine.Pin(0), sda=machine.Pin(4))
    dsp = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, 0x3c, False)

    uart = machine.UART(1, rx=12, tx=15, baudrate=9600, bits=8, parity=None, stop=1, timeout=5000, rxbuf=1024)

    gps = MicropyGPS()

    while True:
      buf = uart.readline()

      for char in buf:
        gps.update(chr(char))  # Note the conversion to to chr, UART outputs ints normally

      #print('UTC Timestamp:', gps.timestamp)
      #print('Date:', gps.date_string('long'))
      #print('Latitude:', gps.latitude)
      #print('Longitude:', gps.longitude_string())
      #print('Horizontal Dilution of Precision:', gps.hdop)
      #print('Altitude:', gps.altitude)
      #print('Satellites:', gps.satellites_in_use)
      #print()
      
      dsp.fill(0)
      y = 0
      dy = 10
      dsp.text("{}".format(gps.date_string('s_mdy')), 0, y)
      dsp.text("Sat:{}".format(gps.satellites_in_use), 80, y)
      y += dy
      dsp.text("{:02d}:{:02d}:{:02.0f}".format(gps.timestamp[0], gps.timestamp[1], gps.timestamp[2]),  0, y)
      y += dy
      dsp.text("Lat:{}{:3d}'{:02.4f}".format(gps.latitude[2], gps.latitude[0], gps.latitude[1]),  0, y)
      y += dy
      dsp.text("Lon:{}{:3d}'{:02.4f}".format(gps.longitude[2], gps.longitude[0], gps.longitude[1]),  0, y)
      y += dy
      dsp.text("Alt:{:0.0f}ft".format(gps.altitude * 1000 / (12*25.4)),  0, y)
      y += dy
      dsp.text("HDP:{:0.2f}".format(gps.hdop),  0, y)
      dsp.show()

 
def startGPSthread():
    _thread.start_new_thread(main, ())

if __name__ == "__main__":
  print('...running main, GPS testing')
  main()
I tried with the code you mentioned.
I did make some changes to it to match my hardware/needs.
My code is below:

Code: Select all

from machine import UART
from micropyGPS import MicropyGPS

uart = UART(2, baudrate=9600, bits=8, parity=None, stop=1)
gps = MicropyGPS()

buf = uart.readline()
print("TEST PRINTING BUFFER:")
print(buf)
for char in buf:
    gps.update(chr(char))  # Note the conversion to to chr, UART outputs ints normally

print('UTC Timestamp:', gps.timestamp)
print('Date:', gps.date_string('long'))
print('Latitude:', gps.latitude)
print('Longitude:', gps.longitude_string())
print('Horizontal Dilution of Precision:', gps.hdop)
print('Altitude:', gps.altitude)
print('Satellites:', gps.satellites_in_use)
print()
I get an error saying "NoneType object isn't iterable" at the "for" statement.
Which if i am understanding correctly means that the "buf" value is Null instead of having a value.
Could you explain how that part of the code works or suggest some solution?


jimmo wrote:
Sun Feb 16, 2020 11:28 pm
nikanta wrote:
Sun Feb 16, 2020 6:57 pm
Then i get 'None' again as data.
The GPS will only send data over the UART periodically, so not every call to read() will return data.

But you also need to do more work to buffer and re-assemble lines. I assume this is what micropygps and as_GPS do.

Yes, that seems to be the case.
I was just hoping for a way to check if gps has data and receive them and then extract only the info i needed(GPRMC) using my code.
I already have made code to extract the info from the NMEA string.
Now i need a way to get the NMEA itself :P

User avatar
jimmo
Posts: 1465
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: ESP32 with Ublox NEO-6M

Post by jimmo » Wed Feb 19, 2020 12:41 am

nikanta wrote:
Tue Feb 18, 2020 1:47 pm
I get an error saying "NoneType object isn't iterable" at the "for" statement.
Which if i am understanding correctly means that the "buf" value is Null instead of having a value.
Yeah. You need to do something like:

Code: Select all

line = uart.readline()
if line:
  for c in line:
    gps.update(c)
(Note you don't need the chr() here -- the line is made up of chars)

but what you probably actually want is:

Code: Select all

while True:
  if uart.any():
    gps.update(chr(uart.read()))
  else:
    # do other things?
(Based on the example in the micropyGPS repo).

User avatar
romeotango
Posts: 27
Joined: Tue Jun 16, 2015 10:52 am
Location: Germany

Re: ESP32 with Ublox NEO-6M

Post by romeotango » Wed Feb 19, 2020 7:13 am

Hello, good morning!
In principle it is quite simple:
Read and interpret the output.
Unfortunately the output of the GPS module is not always correct and above all not complete.
Cleaning up the values is an essential part of the work.
Peter Hinch has thought a lot about this. And even more important: he has documented it really well.
Before you start dealing with recurring problems with the data stream, read
https://github.com/peterhinch/micropython-async/gps.

The methods of data preparation are understandable without asyncio, especially because they are well documented in the program.

By the way: The data from the GPS module are read as bytecode with readline(), so they have to be converted first. This is also very well explained there.

Please always keep in mind that a GPS module needs a long time to provide correct position data, especially under 'non free sky' conditions.

RT

User avatar
pythoncoder
Posts: 4037
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: ESP32 with Ublox NEO-6M

Post by pythoncoder » Wed Feb 19, 2020 7:43 am

nikanta wrote:
Sun Feb 16, 2020 6:57 pm
...
I saw the github link.
And yes it does work and very nicely indeed.
I am trying to get the NMEA data and then edit them myself.
...
Rather than starting from scratch can I suggest an alternative approach? Take the working solution and adapt its NMEA parsing to suit your requirements.
Peter Hinch

nikanta
Posts: 9
Joined: Sun Jun 16, 2019 10:28 am

Re: ESP32 with Ublox NEO-6M

Post by nikanta » Fri Feb 21, 2020 3:28 pm

So i ended up doing something like that:

Code: Select all

uart = UART(2, baudrate=9600, bits=8, parity=None, stop=1)
line = uart.readline()

#tries to get value from uart around every second for 10 seconds or less
#if it gets data it checks if GPRMC is present and it breaks the for function if it is
for x in range(10):
    line = uart.readline()
    time.sleep(1)
    if("GPRMC" in str(line)):
        #print("FOUND GPRMC")
        break

x = str(line).split("\r\n")
for i in range(len(x)):
    if("$GPRMC" in x[i]):
        counter = i
        splitted = x[counter].split(",")

#if statements for calculating lat/long in case we get value 28 or 028
#lat
if(int(splitted[3][0:2])<10):
    latfinal = float(splitted[3][0:3]) + float(splitted[3][3:])/60
else:
    latfinal = float(splitted[3][0:2]) + float(splitted[3][2:])/60
print("Latitude is: " + str(latfinal))

#lon
if(int(splitted[5][0:2])<10):
    longfinal = float(splitted[5][0:3]) + float(splitted[5][3:])/60
else:
    longfinal = float(splitted[5][0:2]) + float(splitted[5][2:])/60
print("Longtitude is: " + str(longfinal))
With that code i get NMEA data nearly every time succesfully.
Maybe the module has gotten a better satelite fix as well compared to the previous days.
I also managed to transform to lat/lon data succesfully.
I get "ValueError: invalid syntax for number" some times at the latfinal and longfinal lines but that shouldn't be anything hard to fix.
Thanks everyone for your help and suggestions. :D

Post Reply