Arduino Due + PMB-648 GPS
Video
Please click thumbnail image to start the video



Excel

Photo



Sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Arduino DUE and the PMB 648 | |
// Copyright (C) 2021 https://www.roboticboat.uk | |
// fc2c01fd-cd04-40f2-a3da-68d56f165c93 | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this program. If not, see <https://www.gnu.org/licenses/>. | |
// These Terms shall be governed and construed in accordance with the laws of | |
// England and Wales, without regard to its conflict of law provisions. | |
// Global variables | |
String inputSerial1 = ""; // a string to hold incoming data | |
boolean IsReadySerial1 = false; // whether the string is complete | |
void setup() | |
{ | |
Serial.println("Initializing GPS"); | |
Serial1.begin(4800); | |
// Set the GPS in walk mode | |
SetGPSWalkMode(); | |
// Reserve 200 bytes for the inputSerial1 string | |
inputSerial1.reserve(200); | |
// Keep the User informed | |
Serial.begin(9600); | |
} | |
void loop() | |
{ | |
// Does Seria11 have a new message? | |
if (IsReadySerial1) | |
{ | |
// Print new message on a new line. | |
// The last character of the inputSerial1 is a new line | |
Serial.print(inputSerial1); | |
// clear the string: | |
inputSerial1 = ""; | |
IsReadySerial1 = false; | |
} | |
} | |
void serialEvent1() | |
{ | |
//This event is called when Serial1 receives new bytes | |
while (Serial1.available() && IsReadySerial1 == false) | |
{ | |
// Read the new byte: | |
char nextChar = (char)Serial1.read(); | |
// Append to the inputSerial1 string | |
inputSerial1 += nextChar; | |
// If a newline, then set flag so that the main loop will process the string | |
if (nextChar == '\n') { | |
IsReadySerial1 = true; | |
} | |
} | |
} | |
void SetGPSWalkMode() | |
{ | |
//The GPS is by default set up as Static Navigation | |
//which means the lat/lon will not change unless moving say 50 meters | |
//The script below turns the GPS to pedestrian/walk mode but need to go via | |
//SiRF binary mode to set the parameters. The GPS will of course must have | |
//the SiRF chipset. | |
// Switch from NMEA to SiRF binary - REQUIRES THE SiRF chip! | |
// $PSRF100 - Message ID | |
// 0 = SiRF binary (1=NMEA) | |
// Band = 4800 | |
// DataBits = 8 | |
// StopBits = 1 | |
// Parity = 0 | |
// Checksum *0C | |
Serial1.println("$PSRF100,0,4800,8,1,0*0F"); //Sets it to SiRF Binary Mode | |
delay(2000); | |
// Static Navigation (Message ID 143) ------------------ | |
// Allows the user to enable or disable static navigation to the receiver | |
// Static model is meant to be used in cars and thus can be turned off | |
Serial1.write((byte)0xA0); // Start Sequence | |
Serial1.write((byte)0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write((byte)0x02); // dec2hex(2 bytes)=0x02 | |
Serial1.write((byte)0x8F); // 1 Message ID = 143 | |
Serial1.write((byte)0x00); // 2 Disable = 0 (Enable = 1) | |
Serial1.write((byte)0x00); // Message Checksum | |
Serial1.write((byte)0x8F); // 0x8F+0x00 (the payload) | |
Serial1.write((byte)0xB0); // End Sequence | |
Serial1.write((byte)0xB3); | |
delay(1000); | |
// Switch to NMEA Protocol (Message ID 129) ------------ | |
Serial1.write((byte)0xA0); // Start Sequence | |
Serial1.write((byte)0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write((byte)0x18); // dec2hex(24 bytes)=0x18 | |
delay(100); | |
Serial1.write((byte)0x81); // byte 1 Message ID = 129 | |
Serial1.write((byte)0x02); // byte 2 Mode | |
Serial1.write((byte)0x01); // byte 3 GGA message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 4 Checksum | |
Serial1.write((byte)0x00); // byte 5 GLL message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 6 Checksum | |
Serial1.write((byte)0x00); // byte 7 GSA message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 8 Checksum | |
Serial1.write((byte)0x00); // byte 9 GSV message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 10 Checksum | |
Serial1.write((byte)0x01); // byte 11 RMC message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 12 Checksum | |
delay(100); | |
Serial1.write((byte)0x00); // byte 13 VTG message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 14 Checksum | |
Serial1.write((byte)0x00); // byte 15 MSS message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 16 Checksum | |
Serial1.write((byte)0x00); // byte 17 EPE message | |
Serial1.write((byte)0x01); // byte 18 Checksum | |
Serial1.write((byte)0x00); // byte 19 ZDA message | |
Serial1.write((byte)0x01); // byte 20 Checksum | |
Serial1.write((byte)0x00); // byte 21 Unused field | |
Serial1.write((byte)0x01); // byte 22 Unused field | |
Serial1.write((byte)0x12); // byte 23 Bit rate dec2hex(4800)=12C0 | |
Serial1.write((byte)0xC0); // byte 24 | |
delay(100); | |
Serial1.write((byte)0x01); // Message Checksum | |
Serial1.write((byte)0x61); // 0x81+0x02+0x01+0x01+0x00+ .... + 0x12+0xC0 | |
Serial1.write((byte)0xB0); // End Sequence | |
Serial1.write((byte)0xB3); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Arduino DUE and the PMB 648 | |
// Copyright (C) 2021 https://www.roboticboat.uk | |
// 76ab2f9e-0397-4f06-9fd7-3671de41d34c | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this program. If not, see <https://www.gnu.org/licenses/>. | |
// These Terms shall be governed and construed in accordance with the laws of | |
// England and Wales, without regard to its conflict of law provisions. | |
float gpstime; | |
float gpsdate; | |
float latitude; | |
float longitude; | |
float altitude; | |
float gpsknots; | |
float gpstrack; | |
char latNS, lonEW; | |
char gpsstatus; | |
int fixquality; | |
int numsatelites; | |
volatile int ptr = 0; | |
volatile bool flag = true; | |
volatile char redbuffer[120]; | |
volatile char blubuffer[120]; | |
void setup() | |
{ | |
// Keep the User informed | |
Serial.begin(9600); | |
Serial.println("Initializing GPS"); | |
Serial1.begin(4800); | |
// Set the GPS in walk mode | |
SetGPSWalkMode(); | |
} | |
void loop() | |
{ | |
Serial.print("GPS,"); | |
Serial.print(gpsdate, 0); | |
Serial.print(","); | |
Serial.print(gpstime, 0); | |
Serial.print(","); | |
Serial.print(latitude, 8); | |
Serial.print(","); | |
Serial.print(latNS); | |
Serial.print(","); | |
Serial.print(longitude, 8); | |
Serial.print(","); | |
Serial.print(lonEW); | |
Serial.print(","); | |
Serial.print(altitude); | |
Serial.print(","); | |
Serial.print(fixquality); | |
Serial.print(","); | |
Serial.print(numsatelites); | |
Serial.print(","); | |
Serial.print(gpsknots); | |
Serial.print(","); | |
Serial.print(gpstrack); | |
Serial.print(","); | |
Serial.println(gpsstatus); | |
} | |
void serialEvent1() | |
{ | |
listen(); | |
} | |
void listen(){ | |
while (Serial1.available()) | |
{ | |
read(Serial1.read()); | |
} | |
} | |
void read(char nextChar){ | |
// Start of a GPS message | |
if (nextChar == '$') { | |
flag ? redbuffer[ptr] = '\0' : blubuffer[ptr] = '\0'; | |
ptr = 0; | |
} | |
// End of a GPS message | |
if (nextChar == '\n') { | |
if (flag) { | |
flag = false; | |
// Set termination character of the current buffer | |
redbuffer[ptr] = '\0'; | |
// Process the message if the checksum is correct | |
if (CheckSum((char*) redbuffer )) {parseString((char*) redbuffer );} | |
} | |
else | |
{ | |
flag = true; | |
// Set termination character of the current buffer | |
blubuffer[ptr] = '\0'; | |
// Process the message if the checksum is correct | |
if (CheckSum((char*) blubuffer )) {parseString((char*) blubuffer );} | |
} | |
// Reset the pointer | |
ptr = 0; | |
} | |
// Add a new character | |
flag ? redbuffer[ptr] = nextChar : blubuffer[ptr] = nextChar; | |
// Check we stay within allocated memory | |
if (ptr < 119) ptr++; | |
} | |
bool CheckSum(char* msg) { | |
// Check the checksum | |
//$GPGGA,.........................0000*6A | |
// Length of the GPS message | |
int len = strlen(msg); | |
// Does it contain the checksum, to check | |
if (len>3 && msg[len-4] == '*') { | |
// Read the checksum from the message | |
int cksum = 16 * Hex2Dec(msg[len-3]) + Hex2Dec(msg[len-2]); | |
// Loop over message characters | |
for (int i=1; i < len-4; i++) { | |
cksum ^= msg[i]; | |
} | |
// The final result should be zero | |
if (cksum == 0){ | |
return true; | |
} | |
} | |
return false; | |
} | |
float DegreeToDecimal(float num, byte sign) | |
{ | |
// Want to convert DDMM.MMMM to a decimal number DD.DDDDD | |
int intpart= (int) num; | |
float decpart = num - intpart; | |
int degree = (int)(intpart / 100); | |
int mins = (int)(intpart % 100); | |
if (sign == 'N' || sign == 'E') | |
{ | |
// Return positive degree | |
return (degree + (mins + decpart)/60); | |
} | |
// Return negative degree | |
return -(degree + (mins + decpart)/60); | |
} | |
void parseString(char* msg) { | |
messageGGA(msg); | |
messageRMC(msg); | |
} | |
void messageGGA(char* msg) | |
{ | |
// Ensure the checksum is correct before doing this | |
// Replace all the commas by end-of-string character '\0' | |
// Read the first string | |
// Knowing the length of the first string, can jump over to the next string | |
// Repeat the process for all the known fields. | |
// Do we have a GGA message? | |
if (!strstr(msg, "GGA")) return; | |
// Length of the GPS message | |
int len = strlen(msg); | |
// Replace all the commas with end character '\0' | |
for (int j=0; j<len; j++){ | |
if (msg[j] == ',' || msg[j] == '*'){ | |
msg[j] = '\0'; | |
} | |
} | |
// Allocate working variables | |
int i = 0; | |
//$GPGGA | |
// GMT time 094728.000 | |
i += strlen(&msg[i])+1; | |
gpstime = atof(&msg[i]); | |
// Latitude | |
i += strlen(&msg[i])+1; | |
latitude = atof(&msg[i]); | |
// North or South (single char) | |
i += strlen(&msg[i])+1; | |
latNS = msg[i]; | |
if (latNS == '\0') latNS = '.'; | |
// Longitude | |
i += strlen(&msg[i])+1; | |
longitude = atof(&msg[i]); | |
// East or West (single char) | |
i += strlen(&msg[i])+1; | |
lonEW = msg[i]; | |
if (lonEW == '\0') lonEW = '.'; | |
// Fix quality (1=GPS)(2=DGPS) | |
i += strlen(&msg[i])+1; | |
fixquality = atof(&msg[i]); | |
// Number of satellites being tracked | |
i += strlen(&msg[i])+1; | |
numsatelites = atoi(&msg[i]); | |
// Horizontal dilution of position | |
i += strlen(&msg[i])+1; | |
// Altitude | |
i += strlen(&msg[i])+1; | |
altitude = atof(&msg[i]); | |
// Height of geoid (mean sea level) | |
i += strlen(&msg[i])+1; | |
// Time in seconds since last DGPS update | |
i += strlen(&msg[i])+1; | |
// DGPS station ID number | |
i += strlen(&msg[i])+1; | |
// Convert from degrees and minutes to degrees in decimals | |
latitude = DegreeToDecimal(latitude, latNS); | |
longitude = DegreeToDecimal(longitude, lonEW); | |
} | |
void messageRMC(char* msg) | |
{ | |
// Ensure the checksum is correct before doing this | |
// Replace all the commas by end-of-string character '\0' | |
// Read the first string | |
// Knowing the length of the first string, can jump over to the next string | |
// Repeat the process for all the known fields. | |
// Do we have a RMC message? | |
if (!strstr(msg, "RMC")) return; | |
// Length of the GPS message | |
int len = strlen(msg); | |
// Replace all the commas with end character '\0' | |
for (int j=0; j<len; j++){ | |
if (msg[j] == ',' || msg[j] == '*'){ | |
msg[j] = '\0'; | |
} | |
} | |
// Allocate working variables | |
int i = 0; | |
//$GPRMC | |
// GMT time 094728.000 | |
i += strlen(&msg[i])+1; | |
gpstime = atof(&msg[i]); | |
// Status A=active or V=Void. | |
i += strlen(&msg[i])+1; | |
gpsstatus = msg[i]; | |
// Latitude | |
i += strlen(&msg[i])+1; | |
latitude = atof(&msg[i]); | |
// North or South (single char) | |
i += strlen(&msg[i])+1; | |
latNS = msg[i]; | |
if (latNS == '\0') latNS = '.'; | |
// Longitude | |
i += strlen(&msg[i])+1; | |
longitude = atof(&msg[i]); | |
// East or West (single char) | |
i += strlen(&msg[i])+1; | |
lonEW = msg[i]; | |
if (lonEW == '\0') lonEW = '.'; | |
// // Speed over the ground in knots | |
i += strlen(&msg[i])+1; | |
gpsknots = atof(&msg[i]); | |
// Track angle in degrees True North | |
i += strlen(&msg[i])+1; | |
gpstrack = atof(&msg[i]); | |
// Date - 31st of March 2018 | |
i += strlen(&msg[i])+1; | |
gpsdate = atof(&msg[i]); | |
// Magnetic Variation | |
// Convert from degrees and minutes to degrees in decimals | |
latitude = DegreeToDecimal(latitude, latNS); | |
longitude = DegreeToDecimal(longitude, lonEW); | |
} | |
// Convert HEX to DEC | |
int Hex2Dec(char c) { | |
if (c >= '0' && c <= '9') { | |
return c - '0'; | |
} | |
else if (c >= 'A' && c <= 'F') { | |
return (c - 'A') + 10; | |
} | |
else { | |
return 0; | |
} | |
} | |
void NMEA2Binary() | |
{ | |
// Switch to Binary communication mode with the GPS Module | |
// Sets it to SiRF Binary Mode | |
Serial1.println("$PSRF100,0,4800,8,1,0*0F"); | |
// Wait for the GPS to think | |
delay(2000); | |
} | |
void Binary2NMEA() | |
{ | |
// Start Sequence | |
Serial1.write(0xA0); | |
Serial1.write(0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write(0x02); // dec2hex(2 bytes)=0x02 | |
Serial1.write(0x87); // Message ID 135 | |
Serial1.write(0x02); // NMEA = 2 | |
// Message Checksum | |
Serial1.write((byte)0x00); | |
Serial1.write(0x89); // 0x8F+0x02 (the payload) | |
// End Sequence | |
Serial1.write(0xB0); | |
Serial1.write(0xB3); | |
} | |
void SetGPSWalkMode() | |
{ | |
//The GPS is by default set up as Static Navigation | |
//which means the lat/lon will not change unless moving say 50 meters | |
//The script below turns the GPS to pedestrian/walk mode but need to go via | |
//SiRF binary mode to set the parameters. The GPS will of course must have | |
//the SiRF chipset. | |
// Switch from NMEA to SiRF binary - REQUIRES THE SiRF chip! | |
// $PSRF100 - Message ID | |
// 0 = SiRF binary (1=NMEA) | |
// Band = 4800 | |
// DataBits = 8 | |
// StopBits = 1 | |
// Parity = 0 | |
// Checksum *0C | |
NMEA2Binary(); | |
// Static Navigation (Message ID 143) ------------------ | |
// Allows the user to enable or disable static navigation to the receiver | |
// Static model is meant to be used in cars and thus can be turned off | |
Serial1.write(0xA0); // Start Sequence | |
Serial1.write(0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write(0x02); // dec2hex(2 bytes)=0x02 | |
Serial1.write(0x8F); // 1 Message ID = 143 | |
Serial1.write((byte)0x00); // 2 Disable = 0 (Enable = 1) | |
Serial1.write((byte)0x00); // Message Checksum | |
Serial1.write(0x8F); // 0x8F+0x00 (the payload) | |
Serial1.write(0xB0); // End Sequence | |
Serial1.write(0xB3); | |
delay(1000); | |
Binary2NMEA(); | |
} | |
void SelectSentences() | |
{ | |
NMEA2Binary(); | |
// Switch to NMEA Protocol (Message ID 129) ------------ | |
Serial1.write((byte)0xA0); // Start Sequence | |
Serial1.write((byte)0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write((byte)0x18); // dec2hex(24 bytes)=0x18 | |
delay(100); | |
Serial1.write((byte)0x81); // byte 1 Message ID = 129 | |
Serial1.write((byte)0x02); // byte 2 Mode | |
Serial1.write((byte)0x01); // byte 3 GGA message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 4 Checksum | |
Serial1.write((byte)0x00); // byte 5 GLL message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 6 Checksum | |
Serial1.write((byte)0x00); // byte 7 GSA message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 8 Checksum | |
Serial1.write((byte)0x00); // byte 9 GSV message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 10 Checksum | |
Serial1.write((byte)0x01); // byte 11 RMC message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 12 Checksum | |
delay(100); | |
Serial1.write((byte)0x00); // byte 13 VTG message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 14 Checksum | |
Serial1.write((byte)0x00); // byte 15 MSS message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 16 Checksum | |
Serial1.write((byte)0x00); // byte 17 EPE message | |
Serial1.write((byte)0x01); // byte 18 Checksum | |
Serial1.write((byte)0x00); // byte 19 ZDA message | |
Serial1.write((byte)0x01); // byte 20 Checksum | |
Serial1.write((byte)0x00); // byte 21 Unused field | |
Serial1.write((byte)0x01); // byte 22 Unused field | |
Serial1.write((byte)0x12); // byte 23 Bit rate dec2hex(4800)=12C0 | |
Serial1.write((byte)0xC0); // byte 24 | |
delay(100); | |
Serial1.write((byte)0x01); // Message Checksum | |
Serial1.write((byte)0x61); // 0x81+0x02+0x01+0x01+0x00+ .... + 0x12+0xC0 | |
Serial1.write((byte)0xB0); // End Sequence | |
Serial1.write((byte)0xB3); | |
//Returns in NMEA format, so no need to request Binary2NMEA | |
} | |
void AllSentences() | |
{ | |
NMEA2Binary(); | |
// Switch to NMEA Protocol (Message ID 129) ------------ | |
Serial1.write((byte)0xA0); // Start Sequence | |
Serial1.write((byte)0xA2); | |
Serial1.write((byte)0x00); // Payload length | |
Serial1.write((byte)0x18); // dec2hex(24 bytes)=0x18 | |
delay(100); | |
Serial1.write((byte)0x81); // byte 1 Message ID = 129 | |
Serial1.write((byte)0x02); // byte 2 Mode | |
Serial1.write((byte)0x01); // byte 3 GGA message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 4 Checksum | |
Serial1.write((byte)0x01); // byte 5 GLL message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 6 Checksum | |
Serial1.write((byte)0x01); // byte 7 GSA message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 8 Checksum | |
Serial1.write((byte)0x05); // byte 9 GSV message (ON) 5 seconds | |
Serial1.write((byte)0x01); // byte 10 Checksum | |
Serial1.write((byte)0x01); // byte 11 RMC message (ON) 1 second | |
Serial1.write((byte)0x01); // byte 12 Checksum | |
delay(100); | |
Serial1.write((byte)0x00); // byte 13 VTG message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 14 Checksum | |
Serial1.write((byte)0x00); // byte 15 MSS message (OFF) 0 second | |
Serial1.write((byte)0x01); // byte 16 Checksum | |
Serial1.write((byte)0x00); // byte 17 EPE message | |
Serial1.write((byte)0x01); // byte 18 Checksum | |
Serial1.write((byte)0x00); // byte 19 ZDA message | |
Serial1.write((byte)0x01); // byte 20 Checksum | |
Serial1.write((byte)0x00); // byte 21 Unused field | |
Serial1.write((byte)0x01); // byte 22 Unused field | |
Serial1.write((byte)0x12); // byte 23 Bit rate dec2hex(4800)=12C0 | |
Serial1.write((byte)0xC0); // byte 24 | |
delay(100); | |
Serial1.write((byte)0x01); // Message Checksum | |
Serial1.write((byte)0x68); // 0x81+0x02+0x01+0x01+0x00+ .... + 0x12+0xC0 | |
Serial1.write((byte)0xB0); // End Sequence | |
Serial1.write((byte)0xB3); | |
//Returns in NMEA format, so no need to request Binary2NMEA | |
} |