B4R Library rGPRMC

rGPRMC is an open source B4R B4XLib for parsing the GPS NMEA RMC sentence message.

Obtain Lat / Lon position, Direction Indicator, Speed, Course an Timestamp.
Addition functions: calculate the distance between, the course to for two Lat / Lon positions or time difference.

Attached
rGPRMC.zip archive contains the B4R library and B4R sample projects.

Install
The files rGPRMC.bx4lib and rGPRMC.xml to be installed in the B4R additional libraries folder.
From the zip archive, copy the rGPRMC.bx4lib and rGPRMC.xml to the B4R additional libraries folder.
B4X:
<path to b4r additional libraries folder>\rGPRMC.bx4lib, rGPRMC.xml
The folder Examples contain some B4R examples (see below Examples).

Hardware
Tested the library with an Arduino UNO and MakerHawk GPS Module with GT-U7 chip and SIM39ES antenna.
MakerHawk Module: Operating frequency: L1 (1575.42 +/- 10MHz), Operating voltage: 3.3 - 5.2V, Operating current: normal 50mA, power-saving 30mA, Communication interface: TTL serial port, micro USB interface (debugging), Serial port baud rate: 9600bps, Communication format: 8N1, Interface logic voltage: 3.3V or 5V, External antenna interface: IPX, Onboard rechargeable button battery, Onboard E2PROM to save parameter data, NEMA output format compatible NEO-6M, Size: 27.6mm * 26.6mm

Wiring
B4X:
GPS Module = Arduino UNO (Wirecolor)
VCC = 3.3V (RED)
GND = GND (BLACK)
RXD = Pin #3 used as TX (BLUE)
TXD = Pin #4 used as RX (GREEN)
PPS = Time Pulse output = Not used - Can be used for the GPS to drive a hardware high precision clock

Syntax Standard
  • Constants are in UPPERCASE - KTTOKMH
  • Fields are in CamelCase - TimeDifference
  • Types start with prefix TGPRMC... - Type TGPRMCTime (Stamp As Double, Hours As Int, Minutes As Int, Seconds As Int)
  • All functions use underscores - Time_Difference(...), Knots_To_Kmh(knots As Double) As Double
Functions

Initialize Module

B4X:
GPRMC.Initialize(Debug_Mode As Boolean, UTC_Offset As Int, Speed_Min_Threshold As Double)
Debug_Mode - Set To True To Log the various steps from init To getting data.
Hint: The debug mode can also set via field GPRMC.DebugMode = True Or False.
UTC_Offset - UTC Offset in Hours (to Local Time) for the Timestamp Hours
Speed_Min_Threshold - Speed Kmh is set to 0 if below min threshold. To disable, set minthreshold 0.
Hint: The threshold can also set via field GPRMC.SpeedMinThresHold = NN.
Returns
None
Example
B4X:
GPRMC.Initialize(False, 2)

Parse NMEA RMC Sentence CSV String into Fields
B4X:
GPRMC.Parse_RMC_Sentence(Data() As Byte) As Boolean
Parse the NMEA RMC sentence fields 0 - 9, which is a CSV string into the fields (see below section Fields).
Data - Byte array holding the NMEA RMC sentence string
Returns
Boolean - True if successfully parsed or field DataValid is True.
Example
B4X:
Dim GPRMC_DATA As String  "$GPRMC,083559.00,A,4717.11437,N,00833.91522,E,10.4,77.52,290621,,,A,V*57"
Dim bc As ByteConverter
GPRMC.Initialize(False)
If GPRMC.Parse_RMC_Sentence(GPRMC_DATA) Then
    Log("Data Valid: ", GPRMC.DataValid)
    Log("Lat: ", GPRMC.Lat.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lat.Indicator)))
    Log("Lon: ", GPRMC.Lon.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lon.Indicator)))
    Log("Speed (kmh): ", GPRMC.Speed, ", Course (deg): ", GPRMC.Course)
    Log("TimeStamp (h.m.s): ", GPRMC.TimeStamp.Hour, ".", GPRMC.TimeStamp.Minutes, ".", GPRMC.TimeStamp.Seconds)
    Log("DateStamp (D.M.Y): ", GPRMC.DateStamp.Day, ".", GPRMC.DateStamp.Month, ".", GPRMC.DateStamp.Year)
End If

Distance (approx in meters) between two Lat Lon Positions
B4X:
GPRMC.Distance_Between_Positions(latA As Double, lonA As Double, latB As Double, lonB As Double, unit As Byte) As Double
Calculate approximate distance between two locations with lat / lon coordinates - NOT ACCURATE.
Best result for distances > 10 Km (to be checked out if smaller distance can do as well properly with f.e. Arduino Leonardo).
latA - Latitude of the first location in decimal degrees (DD.DDDDDD)
lonA - Longitude of the first location in decimal degrees (DD.DDDDDD)
latB - Latitude of the second location in decimal degrees (DD.DDDDDD)
lonB - Longitude of the second location in decimal degrees (DD.DDDDDD)
unit - 0 statute miles, 1 kilometers, 2 nautical miles
Returns
Double - Distance NN.NNNN for the selected unit.
Example
B4X:
'Distance between the cities Hamburg and Kiel (Germany) = 86.5038 km
Log(GPRMC.Distance_Between(53.551086, 9.993682, 54.323334, 10.1394444, 1))

Distance (approx in meters) between two GPS timestamps
B4X:
Distance_Between_TimeStamps(speedA As Double, speedB As Double, timeA As TGPRMCTime, timeB As TGPRMCTime) As Double
Calculate approximate distance in Km using average speed between two GPS timestamps - NOT ACCURATE.
The usual time difference in seconds between two GPS readings is 1 second (depends GPS device and atenna).
speedA - First Speed Kmh
speedB - Second Speed Kmh
timeA - Start time stamp
timeB - End time stamp
Returns
Distance in Km
Example
B4X:
' Time difference is 1 second between two GPS readings. Speed A=50, B=52, Avg=51.
Dim TimeA As TGPRMCTime
TimeA.Hours = 10: TimeA.Minutes=10: TimeA.Seconds=10
Dim TimeB As TGPRMCTime
TimeB.Hours = 10: TimeB.Minutes=10: TimeB.Seconds=11
Log(GPRMC.Distance_Between_TimeStamps(50, 52, TimeA, TimeB))
'0.0142km

Bearing or Course To (approx in degrees) between two Lat Lon Positions
B4X:
GPRMC.Course_To(latA As Double, lonA As Double, latB As Double, lonB As Double) As Double
Calculate approximate bearing (course to) between two locations with lat / lon coordinates - NOT ACCURATE.
latA - Latitude first location in decimal degrees (DD.DDDDDD)
lonA - Longitude first location in decimal degrees (DD.DDDDDD)
latB - Latitude second location in decimal degrees (DD.DDDDDD)
lonB - Longitude second location in decimal degrees (DD.DDDDDD)
Returns
Double - NN.NNNN in degrees decimal
Example
B4X:
'Bearing (=Course to) between the cities Hamburg and Kiel (Germany) = 6.2814 degrees
Log(GPRMC.Course_To(53.551086, 9.993682, 54.323334, 10.1394444))

Time Difference Start and End Time
B4X:
Time_Difference(StartHours As Int, StartMinutes As Int, StartSeconds As Int, EndHours As Int, EndMinutes As Int, EndSeconds As Int)
Calculate the time difference in HH MM SS between a start and end time (to used for duration calculations).
StartHours, StartMinutes, StartSeconds - Int
EndHours, EndMinutes, EndSeconds - Int
Returns
Global variable TimeDifference with type TGPRMCTime is updated.
Note: The time difference is always positive even if the endtime smaller starttime.
Example
B4X:
GPRMC.Time_Difference(10, 55, 1, 11, 2, 3)
Log("Time Difference: ", GPRMC.TimeDifference.Stamp, ",",GPRMC.TimeDifference.Hours,",",GPRMC.TimeDifference.Minutes,",",GPRMC.TimeDifference.Seconds)
' Time Difference: 702,0,7,2

Conversions
Knots to Kmh - 1 knot (kt) = 1.85200 kmh
Kmh to Knots - 1 kmh = 0,539957 knot (kt)
B4X:
Knots_To_Kmh(knots As Double) As Double
Kmh_To_Knots(kmh As Double) As Double

Fields
  • DateStamp.Stamp - Double, Day, Month, Year - Int, UTC
  • TimeStamp.Stamp - Double, Hours, Minutes, Seconds - Int, UTC
  • DataValid - True or False
  • Lat.Degrees, Minutes, Seconds - Int
  • Lat.DegreesDec - Double - Decimal Degrees can be used directly in f.e. Google Maps 47.2852,8.5653
  • Lat.Indicator (N, S) - Byte - Convert to string with ByteConverter bc.StringFromBytes(Array As Byte(GPRMC.Lat.Indicator))
  • Lon.Degrees, Minutes, Seconds - Int
  • Lon.DegreesDec - Double
  • Lon.Indicator (E, W) - Byte
  • Speed - Kmh - Double
  • Course - Degrees over ground - Double
Note
The RMC sentence fields 10 - 14 are not parsed.

Parameter
  • UTCOffSet - Int - UTC Offset in Hours (to Local Time) for the Timestamp Hours.
  • SpeedMinThresHold - Double - Speed Kmh is set to 0 if below min threshold. To disable, set minthreshold 0.
B4R Examples
  • ParseMessage - Test parsing NMEA GPRMC sentence message into fields. There is no GPS module connected.
  • MakerHawk - Test reading & parsing NMEA GPRMC sentence messages from a MakerHawk GPS Module with GT-U7 chip and SIM39ES antenna.
  • LCDDisplay - Mini dashboard showing Lat/Lon position, Speed, Distance, Duration, Actual Time (see post #2 & 3).
MakerHawk
B4X:
Sub Process_Globals
    ' Serial line
    Public serialLine As Serial
    Private Const SERIALLINE_BAUD As ULong = 115200
    ' Software serial port used for GPS module with baud rate 9600
    Private serialGPS As SoftwareSerial
    Private Const SERIALGPS_BAUD As ULong    = 9600        'Take from GPS module datasheet
    Private Const SERIALGPS_RX_PIN As Byte    = 4            'Microcontroller RX pin
    Private Const SERIALGPS_TX_PIN As Byte    = 3            'Microcontroller TX pin
    Private CONST GPRMC_SENTENCE() As Byte    = "$GPRMC"    'As defined by NMEA spec
    ' Handle GPS data received via serial gps
    Private asyncStream As AsyncStreams
    ' Helper to get GPRMC Lat indicator as String in the log
    Private bc As ByteConverter
End Sub

Private Sub AppStart
    serialLine.Initialize(SERIALLINE_BAUD)
    Log("GPRMC Test - GPS MakerHawk Module v20210704")
    'Init the GPS object
    GPRMC.Initialize(False, 2, 0)
    'Init serial GPS with Baud, Arduino RX Pin 4, Arduino TX Pin 3
    serialGPS.Initialize(SERIALGPS_BAUD, SERIALGPS_RX_PIN, SERIALGPS_TX_PIN)
    Log("Init serial GPS")
    asyncStream.Initialize(serialGPS.Stream,"AsyncStream_NewData",Null)
    Log("Init asyncstream, waiting for data... (approx every second)")
End Sub

' Handle new data from the serial GPS line
Sub AsyncStream_NewData(rmcData() As Byte)
    ' Check if the incoming data starts with the NMEA GPRMC sentence
    If bc.IndexOf2(rmcData, GPRMC_SENTENCE, 0) > -1 Then
        Log(rmcData)
        ' Parse the RMC sentence data - the first 10 fields
        If GPRMC.Parse_RMC_Sentence(rmcData) Then
            If GPRMC.DataValid Then
                Log("DateStamp (D.M.Y): ", GPRMC.DateStamp.Stamp, "=", GPRMC.DateStamp.Day, ".", GPRMC.DateStamp.Month, ".", GPRMC.DateStamp.Year)
                Log("TimeStamp (h:m:s): ", GPRMC.TimeStamp.Stamp, "=", GPRMC.TimeStamp.Hours, ":", GPRMC.TimeStamp.Minutes, ":", GPRMC.TimeStamp.Seconds)
                Log("Lat: ", GPRMC.Lat.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lat.Indicator)))
                Log("Lon: ", GPRMC.Lon.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lon.Indicator)))
                Log("Speed (kmh): ", GPRMC.Speed, ", ", GPRMC.Speed * GPRMC.KMHTOKT, " (kt)")
                Log("Course Over Ground (deg): ", GPRMC.Course)
                Log("********************************************************************")
            Else
                ' If not receiving GPS signal, the data valid flag is 0 (false)
                Log("Data is NOT valid")
            End If
        End If
    End If
    ' Short delay to ensure parsing is complete befor new data arrives
    DelayMicroseconds(100)
End Sub

B4R Log Snippet (Debug=False)
B4X:
$GPRMC,082243.00,A,5338.25112,N,00947.94171,E,0.245,,010721,,,A*71
$GPVTG,,T,,M,0.245,N,0.453,K,A*2
DateStamp (D.M.Y): 10721=1.7.21
TimeStamp (h:m:s): 82243=8:22:43
Lat: 53.6375, Indicator: N
Lon: 9.7990, Indicator: E
Speed (kmh): 0.4537, 0.2450 (kt)
Course Over Ground (deg): 0

Hints
  • It can take up-to few minutes (module onboard red LED lights steady) till the module receives GPS data (module onboard red LED starts to blink).
  • If GPS satellite connection made, data is updated almost every second.
  • If testing inhouse, ensure the antenna is close pointing to an outside area.
  • For external power - DC 9V, 2A is required - tested with an Arduino UNO and the MakerHawk.
  • The accuracy of method Distance_Between_Positions improves the longer the distance - depends also on the precision of the microcontroller used.
Licence
GNU General Public License v3.0.

Credits
The formulas used for the
  • method "Distance_Between_Positions" are based on the VB example by GeoDataSource.
  • methods "Distance_Between_Positions2" (currently not used, kept for reference) and "Course_To" are based on the work by and courtesy of Maarten Lamers, The Netherlands.
ToDo
See file TODO.md.

Changelog
v1.45 (20210705) - New function Distance_Between_TimeStamps; Reworked and renamed: Distance_Between to Distance_Between_Positions; FIX: Distance_Between_Positions and Course_To if position not changed; Update LCD Display Example.
v1.40 (20210704) - New functions Time_Difference, Kmh_To_Knots, Knots_To_Kmh; New parameter UTCOffset, SpeedMinThreshold; Changed names types & functions; New example LCDDisplay (see post #2).
v1.00 (20210702) - First version.
See file CHANGELOG.md.
 

Attachments

  • rGPRMC-145.zip
    36.6 KB · Views: 379
Last edited:

rwblinn

Well-Known Member
Licensed User
Longtime User
Update v1.41 (20210704) - See post #1
  • NEW: Example LCDDisplay - Mini dashboard showing Lat/Lon position, Speed, Distance, Duration, Actual Time.
  • NEW: Field UTCOffset - Set local time UTC offset - parameter sub Initialize.
  • NEW: Field SpeedMinThreshold - Speed Kmh is set to 0, if below threshold - parameter sub Initialize.
  • NEW: Sub Kmh_To_Knots - Convert Kmh to Knots
  • NEW: Sub Knots_To_Kmh - Convert Knots to Kmh
  • NEW: Sub Time_Difference - Calculate time difference HH MM SS between two time stamps. Used for duration calculations.
  • UPD: Changed module type names T... to TGPRMC... (i.e. TDate to TGPRMCDate) to ensure Type uniqueness in list of variables/types/constants (avoid clash with other modules).
  • UPD: Changed function names to separate from variable names. All function have underscores: Parse_RMC_Sentence, Distance_Between, Bearing renamed to Course_To.
  • UPD: Changed Type Member TGPRMCTime.Hour to TGPRMCTime.Hours.
  • UPD: All Examples.
Example LCDDisplay aka MiniDashboard

1625389552212.png


B4X:
Sub Process_Globals
    ' Serial line
    Public serialLine As Serial
    Private Const SERIALLINE_BAUD As ULong = 115200
    ' Software serial port used for GPS module with baud rate 9600
    Private serialGPS As SoftwareSerial
    Private Const SERIALGPS_BAUD As ULong    = 9600        'Take from GPS module datasheet
    Private Const SERIALGPS_RX_PIN As Byte    = 4            'Microcontroller RX pin
    Private Const SERIALGPS_TX_PIN As Byte    = 3            'Microcontroller TX pin
    Private CONST GPRMC_SENTENCE() As Byte    = "$GPRMC"    'As defined by NMEA spec
    ' Handle GPS data received via serial gps
    Private asyncStream As AsyncStreams
    Private LatPrev As Double
    Private LonPrev As Double
    Private DistanceTotal As Double = 0
    Private TimeStartHours As ULong = 0
    Private TimeStartMinutes As ULong = 0
    Private TimeStartSeconds As ULong = 0
    Private FirstTime As Boolean = True
    ' LCD 2004
    Private lcd As LiquidCrystalI2CEX
    ' Helper to get GPRMC Lat indicator as String in the log
    Private bc As ByteConverter
End Sub

Private Sub AppStart
    serialLine.Initialize(SERIALLINE_BAUD)
    Log("GPRMC Test - LCD Display v20210704")
    'Init the LCD: Address (1602 = 0x3F,2004 = 0x27), Columns, Rows
    lcd.Initialize(0x27, 20, 4)
    'lcd.Initialize(0x3F, 16, 2)

    'Turn the backlight on (default is turned off), set cursor 0,0, write some text
    lcd.Backlight = True
    lcd.Clear
    lcd.WriteAt(0, 0, "GPRMC Test")
    lcd.WriteAt(0, 2, "Waiting for data ...")
    'Init the GPS object
    GPRMC.Initialize(False, 2, 2)
    'Init serial GPS with Baud, Arduino RX Pin 4, Arduino TX Pin 3
    serialGPS.Initialize(SERIALGPS_BAUD, SERIALGPS_RX_PIN, SERIALGPS_TX_PIN)
    Log("Init serial GPS")
    asyncStream.Initialize(serialGPS.Stream,"AsyncStream_NewData",Null)
    Log("Init asyncstream, waiting for data... (approx every second)")
End Sub

' Handle new data from the serial GPS line
Sub AsyncStream_NewData(rmcData() As Byte)
    ' Check if the incoming data starts with the NMEA GPRMC sentence
    If bc.IndexOf2(rmcData, GPRMC_SENTENCE, 0) > -1 Then
        Log(rmcData)
        ' Parse the RMC sentence data - the first 10 fields
        If GPRMC.Parse_RMC_Sentence(rmcData) Then
            If GPRMC.DataValid Then
                LatPrev = GPRMC.Lat.DegreesDec
                LonPrev = GPRMC.Lon.DegreesDec
              
                Log("DateStamp (D.M.Y): ", GPRMC.DateStamp.Stamp, "=", GPRMC.DateStamp.Day, ".", GPRMC.DateStamp.Month, ".", GPRMC.DateStamp.Year)
                Log("TimeStamp (h:m:s): ", GPRMC.TimeStamp.Stamp, "=", GPRMC.TimeStamp.Hours, ":", GPRMC.TimeStamp.Minutes, ":", GPRMC.TimeStamp.Seconds)
                Log("Lat: ", GPRMC.Lat.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lat.Indicator)))
                Log("Lon: ", GPRMC.Lon.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lon.Indicator)))
                Log("Speed (kmh): ", GPRMC.Speed, ", ", GPRMC.Speed * GPRMC.KMHTOKT, " (kt)")
                Log("Course Over Ground (deg): ", GPRMC.Course)
                Log("********************************************************************")

                ' Handle FirstTime: Reset the flag and assing the timestamp = used for the duration calculation
                If FirstTime Then
                    FirstTime = False
                    TimeStartHours         = GPRMC.TimeStamp.Hours
                    TimeStartMinutes     = GPRMC.TimeStamp.Minutes
                    TimeStartSeconds     = GPRMC.TimeStamp.Seconds
                    ' Log("FirstTime:", TimeStartHours, TimeStartMinutes, TimeStartSeconds)
                    DistanceTotal = 0
                    lcd.Clear
                End If

                ' Write to the LCD display
                lcd.WriteAt(0,0, "Lat|Lon")
                lcd.WriteAt(0,1, GPRMC.Lat.DegreesDec)
                lcd.WriteAt(0,2, GPRMC.Lon.DegreesDec)

                lcd.WriteAt(8,0, "Kmh|Km|Du")
                lcd.WriteAt(10,1, NumberFormat(GPRMC.Speed, 0, 0))
                DistanceTotal = DistanceTotal + GPRMC.Distance_Between(LatPrev, LonPrev, GPRMC.Lat.DegreesDec, GPRMC.Lon.DegreesDec)
                Log("Distance Total: ", DistanceTotal)
                lcd.WriteAt(10,2, NumberFormat(DistanceTotal, 0, 1))

                ' Display the duration in HHMMSS between start and end (= current) time
                GPRMC.Time_Difference(TimeStartHours, TimeStartMinutes, TimeStartSeconds, GPRMC.TimeStamp.Hours, GPRMC.TimeStamp.Minutes, GPRMC.TimeStamp.Seconds)
                lcd.WriteAt(8,3,NumberFormat(GPRMC.TimeDifference.Hours, 2, 0))
                lcd.WriteAt(10,3,".")
                lcd.WriteAt(11,3,NumberFormat(GPRMC.TimeDifference.Minutes, 2, 0))
                lcd.WriteAt(13,3,".")
                lcd.WriteAt(14,3,NumberFormat(GPRMC.TimeDifference.Seconds, 2, 0))

                ' Display the time from the RMC sentence
                lcd.WriteAt(18, 0, "Tm")
                lcd.WriteAt(18, 1, NumberFormat(GPRMC.TimeStamp.Hours, 2, 0))
                lcd.WriteAt(18, 2, NumberFormat(GPRMC.TimeStamp.Minutes, 2, 0))
                lcd.WriteAt(18, 3, NumberFormat(GPRMC.TimeStamp.Seconds, 2, 0))
              
                ' Reset Error
                lcd.WriteAt(0, 3, " ")
            Else
                ' If not receiving GPS signal, the data valid flag is 0 (false)
                Log("ERR: Data NOT valid")
                If Not(FirstTime) then lcd.WriteAt(0, 3, "E")
                ' Option reset because became nan data
                ' FirstTime = True
            End If
        End If
    End If
    ' Short delay to ensure parsing is complete befor new data arrives
    DelayMicroseconds(100)
End Sub
Uses the library rLiquidCrytalI2CEx.
 
Last edited:

rwblinn

Well-Known Member
Licensed User
Longtime User
Update v1.45 (20210705) - See post #1
  • FIX: Distance_Between_Positions and Course_To if position not changed then result 0.
  • NEW: Distance_Between_TimeStamps calculated using avg speed and seconds between two GPS timestamps.
  • UPD: LCD Display Example - Mini Dashboard - using Distance_Between_TimeStamps; Distance with 2 digits; Redesigned layout.
  • UPD: Reworked and renamed Distance_Between to Distance_Between_Positions.
Example LCDDisplay aka MiniDashboard
Bit more fun = based on the prototype post #2, created an other prototype with same hardware (build in an acryl box from Tinkerforge) for tests in my car.
As a result of the tests, made a few code fixes and revised the LCD display layout. Duration based on speed & time difference. Updates every second.
Placed the prototype in the car middle console below the front window, powered by car USB from middle console.
// Next is to test on my Vespa Primavera 50 - which has an USB connector.
1625504730335.png


B4X:
Sub Process_Globals
    ' Serial line
    Public serialLine As Serial
    Private Const SERIALLINE_BAUD As ULong    = 115200
    ' Software serial port used for GPS module with baud rate 9600
    Private serialGPS As SoftwareSerial
    Private Const SERIALGPS_BAUD As ULong    = 9600        'Take from GPS module datasheet
    Private Const SERIALGPS_RX_PIN As Byte    = 4            'Microcontroller RX pin
    Private Const SERIALGPS_TX_PIN As Byte    = 3            'Microcontroller TX pin
    Private CONST GPRMC_SENTENCE() As Byte    = "$GPRMC"    'As defined by NMEA spec
    ' Handle GPS data received via serial gps
    Private asyncStream As AsyncStreams
    Private DistanceTotal As Double
    Private TimeStartHours As ULong = 0
    Private TimeStartMinutes As ULong = 0
    Private TimeStartSeconds As ULong = 0
    Private FirstTime As Boolean = True
    Private TimePrevHours As Int   
    Private TimePrevMinutes As Int   
    Private TimePrevSeconds As Int
    Private SpeedPrev As Double
    ' LCD 2004
    Private lcd As LiquidCrystalI2CEX
    ' Helper to get GPRMC Lat indicator as String in the log
    Private bc As ByteConverter
End Sub

Private Sub AppStart
    serialLine.Initialize(SERIALLINE_BAUD)
    Log("GPRMC Test - LCD Display v20210705")
    'Init the LCD: Address (1602 = 0x3F,2004 = 0x27), Columns, Rows
    lcd.Initialize(0x27, 20, 4)
    'lcd.Initialize(0x3F, 16, 2)

    'Turn the backlight on (default is turned off), set cursor 0,0, write some text
    lcd.Backlight = True
    lcd.Clear
    lcd.WriteAt(0, 0, "GPRMC Test")
    lcd.WriteAt(15, 0, NumberFormat(GPRMC.VERSIONNR, 0, 2) )
    lcd.WriteAt(0, 2, "Waiting for data ...")
    'Init the GPS object with no debug, UTC offset 2 hours, GPS speed min threshold (noise) 3.5 kmh
    GPRMC.Initialize(False, 2, 3.5)
    'Init serial GPS with Baud, Arduino RX Pin 4, Arduino TX Pin 3
    serialGPS.Initialize(SERIALGPS_BAUD, SERIALGPS_RX_PIN, SERIALGPS_TX_PIN)
    Log("Init serial GPS")
    asyncStream.Initialize(serialGPS.Stream,"AsyncStream_NewData",Null)
    Log("Init asyncstream, waiting for data... (approx every second)")
End Sub

' Handle new data from the serial GPS line
Sub AsyncStream_NewData(rmcData() As Byte)
    ' Check if the incoming data starts with the NMEA GPRMC sentence
    If bc.IndexOf2(rmcData, GPRMC_SENTENCE, 0) > -1 Then
        Log(rmcData)
        ' Parse the RMC sentence data - the first 10 fields
        If GPRMC.Parse_RMC_Sentence(rmcData) Then
            If GPRMC.DataValid Then
                Log("DateStamp (D.M.Y): ", GPRMC.DateStamp.Stamp, "=", GPRMC.DateStamp.Day, ".", GPRMC.DateStamp.Month, ".", GPRMC.DateStamp.Year)
                Log("TimeStamp (h:m:s): ", GPRMC.TimeStamp.Stamp, "=", GPRMC.TimeStamp.Hours, ":", GPRMC.TimeStamp.Minutes, ":", GPRMC.TimeStamp.Seconds)
                Log("Lat: ", GPRMC.Lat.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lat.Indicator)))
                Log("Lon: ", GPRMC.Lon.DegreesDec, ", Indicator: ", bc.StringFromBytes(Array As Byte(GPRMC.Lon.Indicator)))
                Log("Speed (kmh): ", GPRMC.Speed, ", ", GPRMC.Speed * GPRMC.KMHTOKT, " (kt)")
                Log("Course Over Ground (deg): ", GPRMC.Course)
                Log("********************************************************************")

                ' Handle FirstTime: Reset the flag and assing the timestamp = used for the duration calculation
                If FirstTime Then
                    FirstTime = False
                    TimeStartHours         = GPRMC.TimeStamp.Hours
                    TimeStartMinutes     = GPRMC.TimeStamp.Minutes
                    TimeStartSeconds     = GPRMC.TimeStamp.Seconds
                    TimePrevHours         = GPRMC.TimeStamp.Hours
                    TimePrevMinutes     = GPRMC.TimeStamp.Minutes
                    TimePrevSeconds     = GPRMC.TimeStamp.Seconds
                    SpeedPrev             = GPRMC.Speed
                    ' Log("FirstTime:", TimeStartHours, TimeStartMinutes, TimeStartSeconds,", Speed:",SpeedPrev)
                    DistanceTotal = 0.0
                    lcd.Clear
                End If

                ' Write to the LCD display

                ' Lat / Lon Position
                lcd.WriteAt(0, 3, GPRMC.Lat.DegreesDec)
                lcd.WriteAt(8, 3, GPRMC.Lon.DegreesDec)

                ' Speed / Distance / Duration
                lcd.WriteAt(0, 0, "Kmh:")
                lcd.WriteAt(5, 0, NumberFormat(GPRMC.Speed, 0, 0))
                ' Speed calculated between twp GPS timestamps - every seconds tested with the MakerHawk
                Dim TimeStampPrev As TGPRMCTime
                TimeStampPrev.Hours        = TimePrevHours
                TimeStampPrev.Minutes    = TimePrevMinutes
                TimeStampPrev.Seconds    = TimePrevSeconds
                ' Log("Distance (km, approx) speed/time based: ", GPRMC.Distance_Between_TimeStamps(SpeedPrev, GPRMC.Speed, TimeStampPrev, GPRMC.TimeStamp))
                DistanceTotal = DistanceTotal + GPRMC.Distance_Between_TimeStamps(SpeedPrev, GPRMC.Speed, TimeStampPrev, GPRMC.TimeStamp)
                ' Log("Distance Total: ", DistanceTotal)
                lcd.WriteAt(0, 1, "Km :")
                lcd.WriteAt(5, 1, NumberFormat(DistanceTotal, 0, 2))
                               
                ' Display the duration in HHMMSS between start and end (= current) time
                GPRMC.Time_Difference(TimeStartHours, TimeStartMinutes, TimeStartSeconds, GPRMC.TimeStamp.Hours, GPRMC.TimeStamp.Minutes, GPRMC.TimeStamp.Seconds)
                lcd.WriteAt(0, 2, "Dur:")
                lcd.WriteAt(5, 2,NumberFormat(GPRMC.TimeDifference.Hours, 2, 0))
                lcd.WriteAt(7, 2,":")
                lcd.WriteAt(8, 2,NumberFormat(GPRMC.TimeDifference.Minutes, 2, 0))
                lcd.WriteAt(10, 2,":")
                lcd.WriteAt(11, 2,NumberFormat(GPRMC.TimeDifference.Seconds, 2, 0))

                ' Display the GPS time from the RMC sentence
                lcd.WriteAt(18, 0, NumberFormat(GPRMC.TimeStamp.Hours, 2, 0))
                lcd.WriteAt(18, 1, NumberFormat(GPRMC.TimeStamp.Minutes, 2, 0))
                lcd.WriteAt(18, 2, NumberFormat(GPRMC.TimeStamp.Seconds, 2, 0))
               
                ' Reset Error Indicator
                lcd.WriteAt(19, 3, " ")

                ' Save the previous data used for the calculations distance and duration
                TimePrevHours         = GPRMC.TimeStamp.Hours
                TimePrevMinutes     = GPRMC.TimeStamp.Minutes
                TimePrevSeconds     = GPRMC.TimeStamp.Seconds
                SpeedPrev             = GPRMC.Speed
            Else
                ' If not receiving GPS signal, the data valid flag is 0 (false)
                Log("ERR: Data NOT valid")
                ' Show E on the display bottom left
                If Not(FirstTime) Then lcd.WriteAt(19, 3, "E")
                ' Option reset for example if received nan data or want to reset the duration
                ' FirstTime = True
            End If
        End If
    End If
    ' Short delay to ensure parsing is complete befor new data arrives
    DelayMicroseconds(100)
End Sub
Uses the library rLiquidCrytalI2CEx.
 
Last edited:
Top