GPS Modules with Arduino and Raspberry Pi |Beitian GPS Module Original manufacturer BN-880 BN-180 BN-220
Today we are learning how to use GPS modules with both the Arduino and Raspberry Pi. We’ll also build a GPS position logger so we can plot our adventures on Google Maps.
GPS is not the only system we’ll be tapping into, as there are other satellite positioning systems that we can use to determine our coordinates. So the proper term would be GNSS, or Global Navigation Satellite System, as there are several satellite positioning systems that we can take advantage of for increased accuracy.
Introduction
GPS and similar systems) really need no introduction, as they have been part of our lives for decades now. Commercial usage started in the mid-90s, and the availability of low-cost circuitry has placed GPS devices into a variety of products, including tablets and phones.
GPS modules provide an accurate measurement of several parameters, including:
- Longitude
- Latitude
- Exact Time
- Altitude
These capabilities open the window to some interesting applications and product ideas and some great experiments for us to perform!
The modules we will be using today are quite inexpensive, but you can also get more expensive GPS modules with advanced features. We will be looking at one of them a bit later.
How Satellite Positioning Systems Work
The principles of triangulation have been known for millennia, and early surveyors used it to map out the world and navigate.
The basics of triangulation are that you have a triangle, and the position you want to measure is at one point on this triangle. In our example that would be person “A”.
Now if both positions “B” and “C” are at known coordinates, and we can measure the length of all our triangle sides, we can determine the exact location of person “A” based upon those measurements.
If person “A” moves into another position we can recalculate, and because the lines are of different lengths now we will be able to calculate the new position.
So part of satellite position has to do with knowing where at least two satellites are and measuring the lines between everything. The “line measurements” are the time delay it takes the radio waves to travel back to earth and our GPS receiver.
Satellite position systems are based upon, what else, satellites! Lots of satellites, whose position is very precisely maintained and updated.
These satellites all contain precision atomic time references, and they transmit timestamps and coordinate information continuously.
Now receiving a signal from just one GPS satellite won’t tell you much. You do know the satellite’s position, and you have its time reference. But that’s not enough to determine your own position, although it is enough to narrow it down to a circle.
In order to determine your position, including your altitude, you actually need a minimum of four satellites
- Three satellites determine the X, Y, and Z coordinates.
- The fourth one serves as a time reference.
In an open area, you’ll be getting signals from dozens of positioning satellites, although your receiver may not be capable of using all of them. Some GPS units are limited in the number of concurrent locks they can make on a group of satellites.
Most GPS modules have some built-in memory to store GPS coordinates and satellite information, so that they can reestablish a lock should the signal become temporarily interrupted, or if the GPS device is powered down for a short while. When they start up they can establish a connection quickly using a “Warm Start”, as opposed to a “Cold Start “ like they do when they first boot up, or after a long period of inactivity.
As I mentioned at the beginning of the article there are many different satellite positioning systems that we can use with our GPS modules.
In fact, the term “GPS module” is in itself inaccurate, as these modules are capable of working with other satellite positioning systems as well. However, I’ll continue to use the term “GPS module” throughout this article, as that is what they are commonly referred to.
Let’s take a look at these satellite positioning systems, starting with the one we are all most familiar with.
GPS (USA)
We’ll start with the most well-known satellite positioning system, the American Global Positioning System, GPS as we all refer to it as.
This system was an evolution of years of military satellite positioning systems, and it was opened up for non-military applications by president Ronald Regan. These were restricted to marine and aviation use initially, but by 1994 the GPS system was available for the general public at no cost, with a 3-meter accuracy limitation.
The GPS system consists of up to 32 satellites and is constantly changing and upgrading, as of this writing there are 31 satellites in use, orbiting the earth twice a day at an altitude of 20,180 Km (12,550 miles).
GLONASS (Russia)
The Russian Global Navigation Satellite System or GLONASS also began life in 1982 as a military navigation satellite program in the USSR. It was made available for commercial use in 1995, the year after GPS became available commercially.
GLONASS consists of 24 satellites orbiting 19,130 Km (11,890 miles) above us.
These satellites can be used in tandem with other positioning systems like GPS to increase accuracy. Usd with GPS, GLONASS can bring the resolution up to about 2.5 meters, as opposed to 3 meters by itself.
BeiDou (China)
The Chinese BeiDou Navigational Satellite system has undergone several iterations. The newest, BeiDou 2, just finished deployment in 2020.
This is a system that employs both medium-orbit and geostationary-orbit satellites.
The 30 medium-orbit satellites reside 21,150 Km (13,140 miles) above the earth. There are also 5 geostationary satellites in the BieDou system.
Galileo (Europe)
The European Space Agencies Galileo system achieved global operation in 2014.
Galileo consists of 30 satellites orbiting at a distance of 23,222 Km (14,429 miles).
The system was designed to complement the US GPS system.
Both India and Japan have established regional satellite positioning systems, between them they cover the majority of Asia.
India’s Navigation with Indian Constellation or NavIC system currently consists of 7 satellites, with plans to increase this to 11. India has signaled its intentions to expand NavIC in the future to become another global satellite positioning service.
Japan’s Quasi-Zenith Satellite System, or QZSS, currently has only one operational satellite. When finished it will have seven.
Interpreting GPS Module Data
Satellites send time and position information, and the data from multiple satellites are used to determine the location in three dimensions, as well as exact time.
GPS modules do most of the “heavy lifting” for you, extracting strings of data from multiple satellites, decoding it, and formatting the results into text strings.
The format of these messages can vary, but most modules use the NMEA 0183 standard. This is a comma-delimited text format that consists of rows of data referred to as “sentences”.
The sentences are provided with an identifier, which itself consists of character patterns, allowing for several dozen string formats.
Here are some of the most popular ones:
- $GPBOD – Bearing, origin to destination
- $GPBWC – Bearing and distance to waypoint, great circle
- $GPGGA – Global Positioning System Fix Data
- $GPGLL – Geographic position, latitude / longitude
- $GPGSA – GPS DOP and active satellites
- $GPGSV – GPS Satellites in view
- $GPHDT – Heading, True
- $GPR00 – List of waypoints in currently active route
- $GPRMA – Recommended minimum specific Loran-C data
- $GPRMB – Recommended minimum navigation info
- $GPRMC – Recommended minimum specific GPS/Transit data
- $GPRTE – Routes
- $GPTRF – Transit Fix Data
- $GPSTN – Multiple Data ID
- $GPVBW – Dual Ground / Water Speed
- $GPVTG – Track made good and ground speed
- $GPWPL – Waypoint location
- $GPXTE – Cross-track error, Measured
- $GPZDA – Date & Time
You can decode NMEA 0183 data manually if you have a lot of time on your hands, but you can also download software to do this tedious task for you.
Here are some examples of data decoded from NMEA strings:
Time – A very accurate time reference, a GPS is a good consideration for your project even if all you need is an accurate clock.
Longitude – Note the results are in degrees.decimal minute format and will probably need to be converted in order to use it.
Latitude – Same thing as longitude, this will probably need to be converted before using it.
Satellites – The number of visible satellites.
Altitude – Measures in meters.
GPS Libraries
A much easier way to work with NMEA 0183 data is to just use a library or program built to use it. And there are many choices of libraries for the Arduino that can parse the GPS data and deliver it to you in a human-readable format.
TinyGPS
A popular library is the TinyGPS Library. You can easily install it, along with a code sample, using the Arduino IDE Library Manager.
- Open your Arduino IDE
- Open the Tools menu (from the top menu bar)
- Look for Manage Libraries on the Tools menu, and click on it
- The Library Manager will open
- Use the filter (search) box to look for “Tiny GPS”
- The TinyGPS by Mikal Hart will display, along with an Install button to the height
- Click the Install button to install the TinyGPS Library
- You can now close the Library Manager.
This is a very popular library, so if you’re running GPS code samples you picked up on the Internet you’ll probably need it.
TinyGPS++
TinyGPS++ is an improved version of TinyGPS, and it comes with several code examples. Unlike TinyGPS, you need to download this library from GitHub as a ZIP file, and then install it into your Arduino IDE manually.
It’s pretty simple:
- Go to the TinyGPSPlus page on GitHub
- Click the green Code button’s drop-down arrow. A sub-menu will appear.
- Click the Download Zip link and save the file somewhere that makes sense to you.
- Now open your Arduino IDE.
- Click on the Sketch menu at the top of the IDE
- Choose Include Library
- From the resulting menu select Add ZIP Library
- Select the ZIP file you downloaded from GitHub
- The Library will be installed
Look over a few of the code samples included with this Library to get a feel for how easy it is to use.
Basic GPS Module Operation – Beitian 180 & 220 with Arduino
Enough theory and libraries, it’s time to actually hook up a GPS module and start getting positioning data!
We will begin with a very basic GPS module. You can use just about any GPS module that outputs serial data, in other words, any module with the following connections:
- Transmit or TX
- Receive or RX
- VCC or 5-volt power supply
- Ground
I’ll be using both the Beitian 180 and 220, which are nearly identical.
Beitian 180 & 220
Price:5.88-23.88USD Click To Shop Now
These units have similar specifications, the 220 is slightly larger and also has internal flash memory, which we aren’t using in our experiments.
The BN-220 has the following specifications (the BN-180 is nearly identical):
- Chipset: 8030-KT
- Receiving Format: GPS,GLONASS,Galileo,BeiDou,QZSS and SBAS
- Frequency: GPS L1,GLONASS L1,BeiDou B1,SBAS L1,Galileo E1
- Channels: 72 Searching Channel
- Position Horizontal: 2.0 m CEP 2D RMS SBAS Enable (Typical Open Sky)
- Velocity: 0.1m/sec 95% (SA off)
- Timing: 1us synchronized to GPS time
Clearly a precision module at a very low price.
You don’t have to use one of these modules if you can’t find one, or if you already have other modules. Any GPS module with a serial data connection that is 5-volt tolerant should work.
Hookup
Here is how we will hook up our GPS module. The connections are very simple.
Once we have it hooked up you’re ready to go satellite hunting! But before you take everything outside it might be a good idea to see if it all works
Second Serial Port
The GPS module has a serial interface, with Receive (RX) and Transmit (TX) connections. This allows the module to be interfaced with just about anything that also has a serial connection.
When it comes to the Arduino Uno we have a bit of a problem. The Uno has only one serial connection, on pins 0 and 1. And while this connection would work perfectly with a GPS module, we can’t use it.
This serial connection is already in use, it’s used by the USB port. So as long as we need the Serial monitor or terminal, we have to leave these pins alone.
We need a second serial connection. Fortunately, it’s pretty easy to emulate one in software.
The SoftwareSerial library is included with your Arduino IDE, so you don’t need to install anything. Simply provide it the baud rate of the serial port you want to emulate and pick two unused digital I/O pins to assign RX and TX to and you’re in business.
SoftwareSerial Test
We will be using SoftwareSerial to emulate a serial port on pins 3 and 4. Since it’s the heart of every sketch we will be using today, it’s nice to have a way to see if it is working (and to assist if it isn’t).
The following sketch is very simple, it just does one thing – it takes data from the software serial port and sends it to the hardware serial port.
The net effect is that if you run the sketch and open your Serial Monitor (be sure to set the baud rate correctly) you’ll be able to read every message delivered to the software serial port.
So if a GPS module is connected to the software serial ports, as we have on pins 3 and 4 here, we should be able to see a bunch of NMEA sentences.
Here is the SoftwareSerial test sketch:
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
|
/*
SoftwareSerial Echo
software-serial-echo.ino
Echos data from SoftwareSerial
Use to display GPS raw data
Results on serial monitor
DroneBot Workshop 2021
https://dronebotworkshop.com
*/
// Define SoftwareSerial Connection
#define swsTX 3 // Transmit FROM GPS
#define swsRX 4 // Receive TO GPS
//GPS Baud rate
#define GPSBaud 9600
//Serial Monitor Baud Rate
#define Serial_Monitor_Baud 115200
// Include and set up the SoftwareSerial Library
#include <SoftwareSerial.h>
SoftwareSerial GPSserial(swsRX, swsTX);
void setup()
{
//Start Serial Monitor
Serial.begin(Serial_Monitor_Baud);
// Start SoftwareSerial
GPSserial.begin(GPSBaud);
}
void loop()
{
// Write SoftwareSerial data to Serial Monitor
while (GPSserial.available() > 0)
Serial.write(GPSserial.read());
}
|
This is a very simple sketch, but it does serve to illustrate how to set up and work with a software serial port.
We start by defining the pins we are using for our software serial port, there is nothing particularly special about the ones I chose so you can change them if you want to. I used pins 3 & 4.
Then we set up the baud rate for the port, which will be the baud rate for the GPS unit. In this example, I used 9600, but you should check with your GPS module documentation for the correct rate.
We also set up the serial monitor baud rate at a quick 115,200, so be sure to adjust your Serial Monitor accordingly when you run the sketch.
Finally, we initialize a software serial object named GPSserial with the pins we defined earlier. We can now use this object as another serial port.
In the Setup, we start both the hardware and software serial ports. And in the Loop we pass any data from the software serial port to the hardware serial port, so we can display it on the serial monitor.
Testing the GPS Module
We can use the sketch to test our GPS module, even if we are indoors where we can’t get a good signal we can still get an output from the GPS module.
Load the sketch and run it, open the serial monitor (check that baud rate!). And observe. You should start seeing NMEA sentences.
Even if you are indoors (as I was) you will still see data, albeit garbage data. If you see nothing, check your wiring. A common problem is reversing the TX and RX pins from the GPS module.
Run the experiment in an area where you have a clear view of the sky. When you first start it may take a minute or two to lock into a satellite, this is a “cold start”. After that your subsequent “warm starts” should be a lot quicker.
You’ll see lines of NMEA data. If you wish you could copy some of it and decode it with a software tool.
But a much easier way of working with NMEA satellite data is to just use a library.
TinyGPS++ Demo
If you installed the TinyGPS++ Library as I suggested earlier then you may have already seen some of the example sketches that come packaged with it.
We will be running the Full Example sketch to demonstrate the TonyGPS++ libraries’ capabilities.
This is one of the sketches included with the TinyGPS++ Library. It gives a wealth of information when fed GPS data. You can get it by going to the File menu , then into Examples, and scrolling down until you see the TinyGPS++ entry.
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
|
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
/*
This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
It requires the use of SoftwareSerial, and assumes that you have a
4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 4800;
// The TinyGPS++ object
TinyGPSPlus gps;
// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);
void setup()
{
Serial.begin(115200);
ss.begin(GPSBaud);
Serial.println(F("FullExample.ino"));
Serial.println(F("An extensive example of many interesting TinyGPS++ features"));
Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
Serial.println(F("by Mikal Hart"));
Serial.println();
Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum"));
Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail"));
Serial.println(F("----------------------------------------------------------------------------------------------------------------------------------------"));
}
void loop()
{
static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
printFloat(gps.hdop.hdop(), gps.hdop.isValid(), 6, 1);
printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
printInt(gps.location.age(), gps.location.isValid(), 5);
printDateTime(gps.date, gps.time);
printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.deg()) : "*** ", 6);
unsigned long distanceKmToLondon =
(unsigned long)TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
LONDON_LAT,
LONDON_LON) / 1000;
printInt(distanceKmToLondon, gps.location.isValid(), 9);
double courseToLondon =
TinyGPSPlus::courseTo(
gps.location.lat(),
gps.location.lng(),
LONDON_LAT,
LONDON_LON);
printFloat(courseToLondon, gps.location.isValid(), 7, 2);
const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon);
printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6);
printInt(gps.charsProcessed(), true, 6);
printInt(gps.sentencesWithFix(), true, 10);
printInt(gps.failedChecksum(), true, 9);
Serial.println();
smartDelay(1000);
if (millis() > 5000 && gps.charsProcessed() < 10)
Serial.println(F("No GPS data received: check wiring"));
}
// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (ss.available())
gps.encode(ss.read());
} while (millis() - start < ms);
}
static void printFloat(float val, bool valid, int len, int prec)
{
if (!valid)
{
while (len-- > 1)
Serial.print('*');
Serial.print(' ');
}
else
{
Serial.print(val, prec);
int vi = abs((int)val);
int flen = prec + (val < 0.0 ? 2 : 1); // . and -
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
for (int i=flen; i<len; ++i)
Serial.print(' ');
}
smartDelay(0);
}
static void printInt(unsigned long val, bool valid, int len)
{
char sz[32] = "*****************";
if (valid)
sprintf(sz, "%ld", val);
sz[len] = 0;
for (int i=strlen(sz); i<len; ++i)
sz[i] = ' ';
if (len > 0)
sz[len-1] = ' ';
Serial.print(sz);
smartDelay(0);
}
static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
if (!d.isValid())
{
Serial.print(F("********** "));
}
else
{
char sz[32];
sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
Serial.print(sz);
}
if (!t.isValid())
{
Serial.print(F("******** "));
}
else
{
char sz[32];
sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
Serial.print(sz);
}
printInt(d.age(), d.isValid(), 5);
smartDelay(0);
}
static void printStr(const char *str, int len)
{
int slen = strlen(str);
for (int i=0; i<len; ++i)
Serial.print(i<slen ? str[i] : ' ');
smartDelay(0);
}
|
The sketch makes use of a number of TinyGPS++ functions to gather data and display it. The data is formatted to make it pretty to print using a number of custom “print” functions. It’s really as simple as that.
Load up the code and take your GPS somewhere that you can receive satellites – you can always test with the previous sketch to be sure you have data coming in.
Now look at the readings as they update, they are pretty accurate. And now you know how far you are from London!
GPS & Compass – Beitian BN-880 with Arduino
The Beitian BN-880 is a very sensitive GPS that also has a built-in magnetometer, meaning that it has a compass inside it. This is very useful in model aeronautics and boating, but it also has possibilities for our robotics projects as well.
Beitian BN-880 5.88-23.88USD Click To Shop Now
The Beitian BN-880 has the following specifications:
- Receiving Format: GPS, GLONASS, Galileo, BeiDou, QZSS and SBAS
- Receiving Channel: 72 Searching Channel
- Output Frequency: 1Hz-10Hz,Default 1Hz
- Support Rate:4800bps to 115200bps,Default 38400dps
- UART Interface: UART Port: TXDA and RXDA
As you can see it’s a pretty powerful unit, and it isn’t all that expensive either.
Magnetometer
The built-in magnetometer is an older model that still sports some impressive specifications.
The magnetometer is based upon a HMC5883L chip, which is billed as a “3-axis Compass”. It is supposed to have a heading accuracy of 1 to 2 degrees, and can be used in conjunction with the GPS readings to tell yo9i not only where you are, but what direction you are heading in.
The HMC5883L is an I2C device, so the BN-880 has two extra connections for the SDA and SCL lines.
Beitian BN-880 Hookup
Here is the hookup for the Beitian BN-880 and an Arduino Uno. Please don’t follow the colors of the wires, as different vendors supply different connection cables. Instead, look at the order of the connections on the BN-880 connector.
You’ll note that the GPS connections for the BN-880 are identical to what they were for the other GPS modules we have already used. You can use the same code to access the GPS section of the BN-880 and it will function perfectly.
The other connections are the I2C connections. In my test, I didn’t need to use pull-up resistors, if you experience erratic readings from the magnetometer you could try using a couple of 4.7k pullups on the SDA and SCL connections.
Magnetometer Code
In order to work with the magnetometer, or compass, feature of the BN-880 we will need to add a library to work with the HMC5883L. There are several, Adafruit has a nice one.
We can install the Adafruit HMC5883L Library from the Library Manager in our Arduino IDE, in the same fashion that we installed the TinyGPS library. Just search the Library Manager for “HMC5883L”, the Adafruit library will likely be the first result.
When you install the Adafruit library for the HMC5883L you may get prompted to install some dependent libraries. Choose to Install All of them, as this library depends upon these other libraries to function (that is actually why it’s named a “unified driver”). You can close the library manager once aloof the unified libraries install.
Once you have the HMC5883L library installed we can run some example software to demonstrate the use of the magnetometer.
Open the list of Examples and scroll down until you see Adafruit HMC5883L Unified. Select it, and select the only sketch in the resulting list – magsensor.
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
|
/***************************************************************************
This is a library example for the HMC5883 magnentometer/compass
Designed specifically to work with the Adafruit HMC5883 Breakout
http://www.adafruit.com/products/1746
*** You will also need to install the Adafruit_Sensor library! ***
These displays use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Kevin Townsend for Adafruit Industries with some heading example from
Love Electronics (loveelectronics.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.
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 <http://www.gnu.org/licenses/>.
***************************************************************************/
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
void displaySensorDetails(void)
{
sensor_t sensor;
mag.getSensor(&sensor);
Serial.println("------------------------------------");
Serial.print ("Sensor: "); Serial.println(sensor.name);
Serial.print ("Driver Ver: "); Serial.println(sensor.version);
Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id);
Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" uT");
Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" uT");
Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" uT");
Serial.println("------------------------------------");
Serial.println("");
delay(500);
}
void setup(void)
{
Serial.begin(9600);
Serial.println("HMC5883 Magnetometer Test"); Serial.println("");
/* Initialise the sensor */
if(!mag.begin())
{
/* There was a problem detecting the HMC5883 ... check your connections */
Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
while(1);
}
/* Display some basic information on this sensor */
displaySensorDetails();
}
void loop(void)
{
/* Get a new sensor event */
sensors_event_t event;
mag.getEvent(&event);
/* Display the results (magnetic vector values are in micro-Tesla (uT)) */
Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print(" ");
Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print(" ");
Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print(" ");Serial.println("uT");
// Hold the module so that Z is pointing 'up' and you can measure the heading with x&y
// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(event.magnetic.y, event.magnetic.x);
// Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
// Mine is: -13* 2' W, which is ~13 Degrees, or (which we need) 0.22 radians
// If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
float declinationAngle = 0.22;
heading += declinationAngle;
// Correct for when signs are reversed.
if(heading < 0)
heading += 2*PI;
// Check for wrap due to addition of declination.
if(heading > 2*PI)
heading -= 2*PI;
// Convert radians to degrees for readability.
float headingDegrees = heading * 180/M_PI;
Serial.print("Heading (degrees): "); Serial.println(headingDegrees);
delay(500);
}
|
This sketch demonstrates the use of the magnetometer by displaying the values from all three degrees of freedom on the serial monitor.
Calculating Magnetic Declination Angle
One thing you may want to note is the “Declination Angle” correction that you can add on line 91, this compensates for the difference between the true north (or south) pole and the magnetic one. It differs depending upon where in the world you are, the value in the sketch is for New York City.
This value is in Radians, so you’ll need to do a little work to get it, as the resources that follow don’t supply a Radians value.
You can calculate your magnetic declination if you know your longitude and latitude, or just by visiting the Magnetic Declination website.
There are several other interesting resources on the Internet for this, as a Canadian may I recommend one from the Government of Canada – it’s useful for anyone, anywhere in the world, not just Canada. And that same Canadian Government has a good article explaining what Magnetic declination actually is.
The NOAA also has a nice online utility that can accept your address and calculate Magnetic declination for you.
However you get it, you’ll need to do two things to it so it can be used in the sketch:
- Convert it to Degrees with decimal places
- Convert Degrees to Radians
The Radians value is what is required for the sketch.
First Convert the Degrees-Minutes-Seconds to Degrees with a decimal figure. Divide the minutes and seconds by 60 to get their decimal values, and add them up.
Then go to a Degree to Radian calculator, like the one Google has.
The resulting value is what you’ll need for your sketch, to correct for magnetic declination in your location.
Magnetometer Demo
Load the sketch onto the Arduino and open the Serial Monitor.
You should see the readings of the X, Y, and Z-axis. Now rotate and twist the sensor, and observe the corresponding readings.
You can experiment and follow the calibration instructions in the code sample to see how accurate a “compass” you can make!
Adafruit Ultimate GPS with Raspberry Pi
The next module we will look at is a bit different in that it uses a USB connection to supply serial data, instead of a couple of output pins.
This sort of sensor is better suited to a microcomputer, so we will be bringing a Raspberry Pi in for this one. Although I use a Raspberry Pi 4 any Pi with internet connectivity, including the model Zero W, would work fine for this task.
Adafruit Ultimate GPS
The Adafruit Ultimate GPS is actually available in two configurations:
- A USB one, like the one I have
- A model with discrete output pins, similar to the other devices we have looked at today.
Both feature the same high-performance GPS sensor.
One real bonus that this module offers is the ability to use an external antenna. I took advantage of this to work on the GPS indoors, with an antenna in my backyard!
The specifications of the Adafruit Ultimate GPS are as follows:
- Satellites: 22 tracking, 66 searching
- Patch Antenna Size: 15mm x 15mm x 4mm
- Update rate: 1 to 10 Hz
- Position Accuracy: < 3 meters (all GPS technology has about 3m accuracy)
- Velocity Accuracy: 0.1 meters/s
- Warm/cold start: 34 seconds
- Acquisition sensitivity: -145 dBm
- Tracking sensitivity: -165 dBm
- Maximum Velocity: 515m/s
- Vin range: 3.0-5.5VDC
- MTK3339 Operating current: 25mA tracking, 20 mA current draw during navigation
- Output: NMEA 0183, 9600 baud default, 3V logic level out, 5V-safe input
- DGPS/WAAS/EGNOS supported
- FCC E911 compliance and AGPS support (Offline mode : EPO valid up to 14 days )
- Up to 210 PRN channels
- Jammer detection and reduction
- Multi-path detection and compensation
You can use the Adafruit Ultimate GPS with a Windows-based utility, the MT3339 GPS Tool. It is an older utility,, but I had no problem getting it to work using Windows 10.
Today, however, we will be using Linux, specifically the Raspberry Pi Operating System.
Adafruit Ultimate GPS Connection to Raspberry Pi
The Adafruit Ultimate GPS uses its microUSB connector for both power and data transfer, so as soon as you plug it into an active USB port it starts working.
While you could certainly use any Internet-connected P{i for the job (you need the Internet to download the software for our demo) the model Zero W has only one USB port, so you would likely need a USB hub.
As I wanted to take advantage of the external antenna jack I purchased a few other accessories.
- An external GPS antenna, I got the Sparkfun
- A metal plate, to thread onto a tripod or light stand and to act as a ground plane for the GPS antenna (it’s a requirement). Your car roof is also a good surface, and the antenna is magnetic.
- A ufl to SMA cable, to connect the tiny ufl connector in the Ultimate GPS to a standard SMA cable, used by most GPS antennas.
- A couple of SMA male to female extension cables. In my case, I had a total of 8 meters of extension cable, and everything worked well.
This allowed me the luxury of using the GPS in my office, but it didn’t quite reach the workshop. So I solved that with a 4 meter USB to microUSB cable to run the “last mile” down my hallway. The GPS module sat in the middle of the hallway, with the Raspberry Pi on my workbench, and it worked well enough to film the demo!
Displaying GPS Data on Raspberry Pi
There are a number of ways to make use of the GPS data on a Raspberry Pi. I’m going to show you a very simple one that you can install and run from the command line.
You’ll need to type six lines at the command line to install the GPSD software and run it – once it’s installed you can run it with just two lines.
I have a Cheat Sheet available, a text file with all of the commands in it. It’s in the ZIP file with all of the code, and you can download the whole package to make your GPS experimenting easier.
Here is what you’ll need to type in:
First, we will list the USB devices, just to be sure we see our Adafruit GPS.
1
|
ls /dev/ttyUSB
|
Next, we will install GPSD, so we can examine incoming GPS data.
1
|
sudo apt-get install gpsd gpsd-clients
|
You’ll be notified of the resources the software will consume and you’ll be asked if you want to proceed, naturally you do, so type “Y”. The software will take a minute to install.
After the GPSD software is installed we need to stop a service it installs, and prevent it from running again. This is a modification required for the Raspberry Pi Operating System, and for some other versions of Linux as well,
First, we stop the service, and then we disable it so it can’t run again:
1
2
|
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socket
|
Now we can manually start the GPSD service and connect it to the GPS on USB0, which is where your Adafruit Ultimate GPS should be.
1
|
sudo systemctl disable gpsd.socket
|
And finally, we can run the GPSD display.
1
|
cgps -s
|
The display may take a minute to lock in if you’re performing a cold start, after that it should happen pretty quickly. You’ll get your position information updated every few seconds.
Build a GPS Position Logger
We’re going to finish off with a fun experiment, one that will get you out of the worship and outside in the fresh air. At least it worked on me!
We are going to build a very simple GPS Position Logger. A device that will log our GPS position and time readings to a mMicroSD (or SD) card, so that we can view them later.
And we can actually “view” them, as the results we are returning are in a format suitable for use with Google Maps!
GPS Position Logger Operation
This is a very simple position logger, you can use it as the basis for a more advanced design. It works as follows:
- Starts up, checks GPS and SD card status
- Blinks LED three times to indicate all is OK
- Attempts to lock onto several satellites. If this is a cold start the process may take a few minutes, a warm start usually locks in a few seconds.
- Reads and validates satellite data
- Records latitude, longitude, and timestamp to the SD card
- Flashes the LED to indicate a successful recording
- Continues until powered down
The data is saved in a CSV, or Comma Separated Value, format. I’m pretty sure you’ve already encountered this standard text format, which can be opened with any spreadsheet application or text editor.
A CSV file can also be used with Google Maps to import map data, so you can get a visualization of where you have traveled to. Note that Google Maps accepts a CSV file with a maximum of 2000 entries.
To prevent “flooding” Google Maps, which I did a few times while prototyping this design, I put a counter into the program. This worked to slow down the CSV file update from several times per second to once every three seconds, which I deemed an adequate compromise and suitable for walking.
There is a variable in the code you can use to set the pace of the update, with my experiment I walked for over 25 minutes and recorded just under 400 lines of data. You may experience slightly different times, due to differences in satellite reception conditions.
GPS Position Logger Components
I just built my logger on a standard solderless breadboard. I advise that you do that before you put together a more permanent version of it. You might come up with some great modifications!
To build the GPS Position Logger you will need the following parts:
- An Arduino Uno, or other 8-bit AVR Arduino board
- A GPS sensor like the Bietain BN-220 (any sensor with a serial output should work)
- An SD or MicroSD card module that runs on 5-volts. One that has a regulator and level-shifter onboard.
- An LED and a dropping resistor, 220 ohms is what I used and I could see my LED flash in broad daylight.
You‘ll also need a way of powering up your project when you take it outside. I just used a 9-volt battery (a fresh one!) and the power jack on the Uno, which is probably the easiest way. You could also use a USB Power bank.
Of course, you’ll need a breadboard or perfboard to build everything, as well as some suitable wire.
GPS Position Logger Hookup
Here is how to hook everything up, it’s essentially the same GPS hookup we have already used with the addition of the SD card module and the LED. The SD card module uses the Arduino’s SPI connection.
You may want to give consideration as to where you mount the GPS module, as that will be our “reference point. I mounted mine at the end of the solderless breadboard, with a couple of wire loops to position it and minimize its movement.
GPS Position Logger Code
Of course, the real fun is in the code, which is presented here:
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
|
/*
GPS Position Logger
gps-position-logger.ino
Read GPS data from BN-220 or any serial GPS sensor
Requires TinyGPS++ Library
Save to SD or microSD card
DroneBot Workshop 2021
https://dronebotworkshop.com
*/
// Include required libraries
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>
// GPS Connections
static const int RXPin = 4, TXPin = 3;
// GPS Baud rate (change if required)
static const uint32_t GPSBaud = 9600;
// SD Card Select pin
const int chipSelect = 5;
// Write LED
const int recLED = 7;
// String to hold GPS data
String gpstext;
// GPS write delay counter variables
// Change gpsttlcount as required
int gpscount = 0;
int gpsttlcount = 30;
// TinyGPS++ object
TinyGPSPlus gps;
// SoftwareSerial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);
void setup()
{
// Set LED pin as output
pinMode(recLED, OUTPUT);
// Start Serial Monitor for debugging
Serial.begin(115200);
// Start SoftwareSerial
ss.begin(GPSBaud);
// Initialize SD card
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
//don't do anything more:
while (1);
}
Serial.println("card initialized.");
// Blink LED so we know we are ready
digitalWrite(recLED, HIGH);
delay(500);
digitalWrite(recLED, LOW);
delay(500);
digitalWrite(recLED, HIGH);
delay(500);
digitalWrite(recLED, LOW);
delay(500);
digitalWrite(recLED, HIGH);
delay(500);
digitalWrite(recLED, LOW);
}
void loop()
{
// Turn off LED
digitalWrite(recLED, LOW);
// See if data available
while (ss.available() > 0)
if (gps.encode(ss.read()))
// See if we have a complete GPS data string
if (displayInfo() != "0")
{
// Get GPS string
gpstext = displayInfo();
// Check GPS Count
Serial.println(gpscount);
if (gpscount == gpsttlcount) {
// LED On to indicate data to write to SD card
digitalWrite(recLED, HIGH);
//Open the file on card for writing
File dataFile = SD.open("gpslog.csv", FILE_WRITE);
if (dataFile) {
// If the file is available, write to it and close the file
dataFile.println(gpstext);
dataFile.close();
// Serial print GPS string for debugging
Serial.println(gpstext);
}
// If the file isn't open print an error message for debugging
else {
Serial.println("error opening datalog.txt");
}
}
// Increment GPS Count
gpscount = gpscount + 1;
if (gpscount > gpsttlcount) {
gpscount = 0;
}
}
}
// Function to return GPS string
String displayInfo()
{
// Define empty string to hold output
String gpsdata = "";
// Get latitude and longitude
if (gps.location.isValid())
{
gpsdata = String(gps.location.lat(), 6);
gpsdata += (",");
gpsdata += String(gps.location.lng(), 6);
gpsdata += (",");
}
else
{
return "0";
}
// Get Date
if (gps.date.isValid())
{
gpsdata += String(gps.date.year());
gpsdata += ("-");
if (gps.date.month() < 10) gpsdata += ("0");
gpsdata += String(gps.date.month());
gpsdata += ("-");
if (gps.date.day() < 10) gpsdata += ("0");
gpsdata += String(gps.date.day());
}
else
{
return "0";
}
// Space between date and time
gpsdata += (" ");
// Get time
if (gps.time.isValid())
{
if (gps.time.hour() < 10) gpsdata += ("0");
gpsdata += String(gps.time.hour());
gpsdata += (":");
if (gps.time.minute() < 10) gpsdata += ("0");
gpsdata += String(gps.time.minute());
gpsdata += (":");
if (gps.time.second() < 10) gpsdata += ("0");
gpsdata += String(gps.time.second());
}
else
{
return "0";
}
// Return completed string
return gpsdata;
}
|
As you can see the code is based upon the TinyGPS++ Library, which you need to install using a ZIP file as described earlier in this text. So if you haven’t done that yet you should do it right now!
The other three libraries are already part of your Arduino IDE, so there is nothing else to install
- The SoftwareSerial library, to create a software serial port for the GPS connection.
- The SPI library, to address the MicroSD card module, as it is an SPI device,
- The SD library, to work with files and directories on the MicroSD or SD card.
We start by defining some constants and variables, most of which are pretty obvious. The SD Card CS lead is defined as pin 5, you can move it if you want to and change the code here.
Same with the LED on pin 7.
The gpstext string holds a line of comma-delimited text to write to the SD or MicroSD card.
The two variables for the gpscount are used to “taper off” the readings, otherwise, you’ll exceed the 2000 points that Google Maps can use very quickly.
Of particular note is the variable gpsttlcount , an integer that determines how many GPS readings we “throw-away”. I have set it to 30, which means I only record one out of every 30 readings.
I found this value suitable for walking, but you can feel free to experiment. You’ll get more readings if you reduce it, and fewer if you increase it.
In the setup, we set up our ports, start the SoftwareSerial and then attempt to initialize the SD card.
If we fail to initialize the SD card then we don’t continue, and we print an error message to the serial monitor.
Since we plan on using this in the field, without a serial monitor, we flash the LED a few times after we successfully initialize the SD card. So if you don’t see the flashing LED a second or two after powering up, then the SD card didn’t initialize.
Before you look at the Loop take a peek further down at a function called displayInfo() . This function returns a string with one of two values:
- A formatted line of text for the CSV file
- The character “0”
The “0” is sent back when the GPS data for location or time isn’t valid, which will happen during a cold start. We don’t want to record those failed attempts.
The function makes extensive use of the TinyGPS++ library to get data for location and time.
Note how the longs used for latitude and longitude are converted to Strings with the String macro. This macro uses an optional parameter to determine decimal places, which in this sketch is “6”.
Also note how time and date digits less than 10 are handled, with a preceding zero inserted to make everything format correctly.
In the Loop we call this function, and if the data is valid we go through the counter. If the count has been reached then we open a file called “gpslog.csv” for writing.
If this file does not exist we will create it. If it does exist we will append data to it. This is important to note if you are using the GPS Logger and turn it off and on again.
If the file opens successfully we will write a line of text and then close the file. We set the counter back to zero and go through it all again.
Load the sketch onto your Arduino and run it, ideally with the serial monitor at first to see if all is working correctly. The serial monitor will print a line of CSV data every time it is written to the MicroSD card.
Once you have it working with the computer you can try it stand-alone, I’d suggest a small local trip to start. Go out and gather some data, and then we’ll see how we can display it.
Reading GPS Position Logger Results with Google Maps
After running the position logger for a while you should be ready to read the data you have gathered.
Power down the Arduino and eject the MicroSD card. Pop it into a computer with a suitable adapter.
You should see one file on your card, GPSLOG.CSV. Note that despite entering our filename in lowercase in the code the SD library has converted it all to upper case.
You might want to copy that CSV file to your local computer, to make it easier to work with.
Reading CSV File
A CSV file is just a text file with values separated by commas, so it can be read in any text editor.
Even better, this is also a format recognized by pretty well every spreadsheet application ever built. So you can open Excel, Google Sheets, or LibreOffice Calc to view the file in a more detailed fashion.
I opened my file in LibreOffice Calc, and it shows three columns.
- Longitude
- Latitude
- Timestamp
Looking at the timestamp on my sample file I determined that I walked for about 26 minutes, during which time I collected 398 lines of data.
Displaying CSV Files on Google Maps
Of course, looking at all this data in a spreadsheet isn’t really very descriptive, unless you happen to have geographic coordinates memorized (I don’t).
There is a much better way to look at our data and see where we have been – Google Maps.
Google Maps can import data in several formats, including a CSV file with three columns, like the one we just saved.
Here is what you need to import GPSLOG.CSV into a Google Map.
- Open Google Maps.
- Click on the Menu (top left corner).
- Click Your Places.
- In the Your Places window click the MAPS tab.
- At the bottom of the Maps tab click the Create Map link. You can also click See All Your Maps and use the red Create a New Map button. Both methods will open a new map.
- You may give your map a title and description, this is optional.
- Click the Import link (under Untitled Layer). The Import dialog box will open.
- In the Import dialog box click the Select a file from your device button.
- Select the GPSLOG.CSV file, either from the SD card or from a local copy.
- The import process will proceed.
- Assign the first column to “latitude”
- Assign the second column to “longitude”. Click to continue
- Use the third column (timestamp) as the name for the waypoint. Click to continue.
- Your waypoints will be imported, and Google will draw you a map of your journey.
My maps looked pretty good, I walked around a park and my trip matched the one Google laid out for me pretty closely.
In some cases, I was under a lot of trees, but it didn’t seem to affect the readings.
This simple position logger could certainly be expanded upon to make a useful tool.
Conclusion
I had a lot of fun playing with these GPS modules, and I was impressed with the performance of even the most inexpensive ones like the BN-180 and BN-220.
I also am doing more work with some fairly high-end GPS tools and will be showing you the fruits of my labor very soon, so stay tuned.
In the meantime why don’t you pick up a GPS module and check it out, if you need precision location or time information, or just want to see how these little wonders work.
Parts List
Here are some components that you might need to complete the experiments in this article. Please note that some of these links may be affiliate links, and the DroneBot Workshop may receive a commission on your purchases. This does not increase the cost to you and is a method of supporting this ad-free website.
COMING SOON!
Resources
Code and Cheat Sheet – All of the code for this article, plus the command-line “cheat sheet” for Raspberry Pi
TinyGPS++ Library Guide – More information about using the TinyGPS++ Library
Adafruit Ultimate GPS Guide – Documentation for the Ultimate GPS (both interfaces) from Adafruit
Find your Magnetic Declination – Find Magnetic declination for your location or any location in the world.
Disclaimer: Thanks to the original author. If there is any infringement, please contact us to delete it.
Article source: https://dronebotworkshop.com/using-gps-modules/
We are the original manufacturer of GPS module.Can be customized for variety fields and projects.Beitian products are widely used in driving test and training, lawn mower, UAV, precision agriculture, automatic driving, measurement and mapping, deformation monitoring, navigation timing and other fields.
Product Series:
GPS Module | GPS Antenna | GPS Receiver | RTK GPS Module | RTK GPS Antenna | RTK GPS Receiver
GNSS Module | GNSS Antenna | GNSS Receiver | RTK GNSS Module | RTK GNSS Antenna | RTK GNSS Receiver