Рассказывать о том, что такое GPS и как работает эта технология я не буду. Этой информации достаточно.
Я расскажу как подключить GPS модуль к Arduino, какие данные нам присылает модуль, что означают эти цифры с запятыми. Также расскажу как получить координаты, дату и время используя популярную библиотеку TinyGPS++ или без использования сторонних библиотек.
В наличии имеются GPS Модули: GOOUUU ATGM336H
и GY-NEO7M
Подключаем к Arduino и считываем данные
Подключаем по следующей схеме
GPS VCC -> MEGA PIN 5V или 3.3V
GPS GND -> MEGA PIN GND
GPS TX -> MEGA RX1 (19)
GPS RX -> MEGA TX1 (18)
Схема подключения для Arduino Uno
GPS VCC -> UNO PIN 5V или 3.3V
GPS GND -> UNO PIN GND
GPS TX -> UNO PIN 2
GPS RX -> UNO PIN 3
Просмотр присылаемых данных с модуля
Время установки соединения со спутниками при первом запуске может занимать до 45 минут, это называется холодный старт.
Модуль загружает альманах и эфемериды. Альманах содержит параметры орбит всех спутников, а эфемериды содержат параметры орбит и часов конкретного спутника, данные альманаха действительны 30 дней, эфемерид 30 минут.
При наличии актуального альманаха и эфемерид, старт происходит гораздо быстрее.
Скетч для вывода данных:
#ifndef HAVE_HWSERIAL1
#include <SoftwareSerial.h>
SoftwareSerial Serial1(2, 3); // RX, TX
#endif
void setup() {
Serial.begin(115200);
Serial1.begin(9600);
Serial.println("Start...");
}
void loop() {
while (Serial1.available() > 0) {
Serial.write(Serial1.read());
}
}
Подробно о данных которые мы получили.
Модуль нам присылает данные в виде NMEA сообщений.
В таком виде получаем данные от GOOUUU ATGM336H
$GNGGA,125354.000,6003.6592,N,03019.6917,E,1,10,0.8,49.0,M,0.0,M,,*4D
$GNGLL,6003.6592,N,03019.6917,E,125354.000,A,A*4C
$GPGSA,A,3,26,27,10,20,16,15,08,13,18,07,,,1.6,0.8,1.4*3D
$BDGSA,A,3,,,,,,,,,,,,,1.6,0.8,1.4*29
$GPGSV,4,1,13,07,11,321,34,08,31,298,40,10,44,182,34,13,19,033,31*76
$GPGSV,4,2,13,15,28,064,34,16,38,228,32,18,43,087,19,20,65,131,31*7A
$GPGSV,4,3,13,21,79,125,,26,13,202,25,27,62,276,41,30,11,351,*75
$GPGSV,4,4,13,33,,,27*7E
$BDGSV,1,1,01,14,,,28*66
$GNRMC,125354.000,A,6003.6592,N,03019.6917,E,0.00,270.98,081020,,,A*7A
$GNVTG,270.98,T,,M,0.00,N,0.00,K,A*27
$GNZDA,125354.000,08,10,2020,00,00*4B
$GPTXT,01,01,01,ANTENNA OK*35
А вот данные от GY-NEO7M
, приходят раз в секунду
$GPRMC,125354.000,A,6003.6592,N,03019.6917,E,0.028,,081020,,,A*79
$GPVTG,,T,,M,0.028,N,0.051,K,A*2D
$GPGGA,125354.000,6003.6592,N,03019.6917,E,1,05,2.06,34.0,M,15.9,M,,*6F
$GPGSA,A,3,07,09,27,08,28,,,,,,,,2.57,2.06,1.54*0F
$GPGSV,3,1,11,05,48,269,,07,74,108,38,08,14,090,32,09,33,125,45*72
$GPGSV,3,2,11,13,23,285,11,16,13,033,19,18,12,349,14,27,20,055,32*7E
$GPGSV,3,3,11,28,09,181,34,30,66,216,,39,22,186,35*40
$GPGLL,6003.6592,N,03019.6917,E,162312.00,A,A*61
Коротко о идентификаторах данных:
- GNGGA,GPGGA — Данные о последнем определении местоположения
- GNGLL,GPGLL — Координаты, широта/долгота
- GPGSA — DOP (GPS) и активные спутники
- GPGSV — наблюдаемые спутники
- GNRMC,GPRMC — Рекомендуемый минимум навигационных данных
Расшифровка значений идентификаторов
GNRMC,GPRMC - Рекомендуемый минимум навигационных данных
$GNRMC,125354.000,A,6003.6592,N,03019.6917,E,0.00,270.98,230620,,,A*7A
$GxRMC | Рекомендуемый минимум навигационных данных |
---|---|
125354.000 | Время UTC (12 часов 53 минут 54 секунды) |
A | Статус (A — данные верны, V —данные не верны) |
6003.6592 | Широта в формате ddmm.mmmm (60° 03.6592’) |
N | Север/Юг (N/S) |
03019.6917 | Долгота в формате ddmm.mmmm (30° 19.6917’) |
E | Запад/Восток (E/W) |
0.00 | Скорость в узлах |
270.98 | Направление движения в градусах (270.98) |
081020 | Дата (8 октября (20)20 года) |
Магнитное склонение в градусах | |
Магнитное склонение на Запад/Восток (E/W) | |
A | Индикатор режима (A — данные верны, N — недостоверные данные) |
*7A | Контрольная сумма |
Данные по другим идентификаторам: GxGGA, GxGLL, GxGSA, GxGSV
GNGGA,GPGGA - Данные о последнем определении местоположения
$GNGGA,125354.000,6003.6592,N,03019.6917,E,1,10,0.8,49.0,M,0.0,M,,*4D
$GxGGA | Данные о последнем определении местоположения |
---|---|
125354.000 | Время UTC (12 часов 53 минут 54 секунды) |
A | Статус (A — данные верны, V —данные не верны) |
6003.6592 | Широта в формате ddmm.mmmm (60° 03.6592’) |
N | Север/Юг (N/S) |
03019.6917 | Долгота в формате ddmm.mmmm (30° 19.6917’) |
E | Запад/Восток (E/W) |
1 | Индикатор качества GPS сигнала 0 - Определение местоположения не возможно 1 - GPS режим обычной точности, возможно определение местоположения 2 - Дифференциальный GPS режим, точность обычная, возможно определение местоположения 3 - GPS режим прецизионной точности, возможно определение местоположения |
10 | Количество используемых спутников |
0.8 | Геометрический фактор, HDOP |
49.0 | Высота над уровнем моря |
M | Единица измерения высоты над уровнем моря, М - метры |
0.0 | Высота геоида над эллипсоидом WGS 84 |
M | Единица измерения высоты геоида над эллипсоидом WGS 84, М - метры |
Время прошедшее с момента получения последней DGPS поправки | |
*4D | Контрольная сумма |
GNGLL,GPGLL - Координаты, широта/долгота
$GNGLL,6003.6592,N,03019.6917,E,125354.000,A,A*4C
$GxGLL | Координаты, широта/долгота |
---|---|
6003.6592 | Широта в формате ddmm.mmmm (60° 03.6592’) |
N | Север/Юг (N/S) |
03019.6917 | Долгота в формате ddmm.mmmm (30° 19.6917’) |
E | Запад/Восток (E/W) |
125354.000 | Время UTC (12 часов 53 минут 54 секунды) |
A | Статус (A — данные верны, V —данные не верны) |
A | Индикатор режима позиционирования A - Автономный D - Дифференциальный E - Примерный M - Ручной N - данные не верны |
*4C | Контрольная сумма |
GPGSA - Общая информация о спутниках
$GPGSA,A,3,26,27,10,20,16,15,08,13,18,07,,,1.6,0.8,1.4*3D
$GxGSA | Координаты, широта/долгота |
---|---|
A | Режим А – Автоматический тип выбора между 2D и 3D режимом M - Ручной, принудительно включен 2D или 3D режим |
3 | Тип режима 1 – Местоположение не определено 2 – 2D режим 3 – 3D режим |
26 | PRN номер спутника GPS = 1 до 32 SBAS = 33 до 64 GLO = 65 to 96 |
27 | PRN номер спутника |
10 | PRN номер спутника |
20 | PRN номер спутника |
16 | PRN номер спутника |
15 | PRN номер спутника |
08 | PRN номер спутника |
13 | PRN номер спутника |
18 | PRN номер спутника |
07 | PRN номер спутника |
PRN номер спутника | |
PRN номер спутника | |
1.6 | PDOP - Position dilution of precision |
0.8 | HDOP - Horizontal dilution of precision |
1.4 | VDOP - Vertical dilution of precision |
*3D | Контрольная сумма |
GPGSV - Детальная информация о спутниках
$GPGSV,4,1,13,07,11,321,34,08,31,298,40,10,44,182,34,13,19,033,31*76
$GxGSV | Детальная информация о спутниках |
---|---|
4 | Количество сообщений GSV в пакете от 1 до 9 |
1 | Номер сообщения в пакете от 1 до 3 |
13 | Полное число видимых спутников |
07 | PRN номер спутника GPS = 1 до 32 SBAS = 33 до 64 GLO = 65 to 96 |
11 | Высота, градусы, 90° максимум |
321 | Азимут истинный, градусы, от 000° до 359° |
34 | SNR, уровень сигнала |
08 | PRN номер спутника Данные второго спутника |
31 | Высота, градусы Данные второго спутника |
298 | Азимут истинный, градусы Данные второго спутника |
40 | SNR, уровень сигнала Данные второго спутника |
10 | PRN номер спутника Данные третьего спутника |
44 | Высота, градусы Данные третьего спутника |
182 | Азимут истинный, градусы Данные третьего спутника |
34 | SNR, уровень сигнала Данные третьего спутника |
13 | PRN номер спутника Данные четвертого спутника |
19 | Высота, градусы Данные четвертого спутника |
033 | Азимут истинный, градусы Данные четвертого спутника |
31 | SNR, уровень сигнала Данные четвертого спутника |
*76 | Контрольная сумма |
GNVTG - Данные о направлении и скорости движения
$GNVTG,270.98,T,,M,0.00,N,0.00,K,A*27
$GxVTG | Данные о направлении и скорости движения |
---|---|
270.98 | Направление движения в градусах |
T | True |
Магнитное склонение | |
M | Магнитное склонение индикатор |
0.00 | Скорость |
N | Единица измерения скорости, узлы (N = Knots) |
0.00 | Скорость |
K | Единица измерения скорости, км/ч (K = km/hr) |
A | Индикатор режима A - Автономный D - Дифференциальный E - Примерный M - Ручной N - данные не верны |
*27 | Контрольная сумма |
GNZDA - Данные о времени, календарном дне, месяце, годе и локальном часовом поясе
$GNZDA,125354.000,08,10,2020,00,00*4B
$GxZDA | Данные о времени, календарном дне, месяце, годе и локальном часовом поясе |
---|---|
125354.000 | Время UTC (12 часов 53 минут 54 секунды) |
08 | День |
10 | Месяц |
2020 | Год |
00 | Часовой пояс, смещение от GMT часы |
00 | Часовой пояс, смещение от GMT минуты |
*4B | Контрольная сумма |
Получение координат, даты и времени
Скетч с использованием библиотеки TinyGPS++
#include <TinyGPS++.h>
#ifndef HAVE_HWSERIAL1
#include <SoftwareSerial.h>
SoftwareSerial Serial1(2, 3); // RX, TX
#endif
static const uint32_t GPSBaud = 9600;
// The TinyGPS++ object
TinyGPSPlus gps;
void setup()
{
Serial.begin(115200);
Serial1.begin(GPSBaud);
}
void loop()
{
// This sketch displays information every time a new sentence is correctly encoded.
while (Serial1.available() > 0)
if (gps.encode(Serial1.read()))
displayInfo();
if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println(F("No GPS detected: check wiring."));
while(true);
}
}
void displayInfo()
{
Serial.print(F("Location: "));
if (gps.location.isValid())
{
Serial.print(gps.location.lat(), 6);
Serial.print(F(","));
Serial.print(gps.location.lng(), 6);
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Speed: "));
if (gps.speed.isValid())
{
Serial.print(gps.speed.kmph(), 2);
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Date/Time: "));
if (gps.date.isValid())
{
Serial.print(gps.date.month());
Serial.print(F("/"));
Serial.print(gps.date.day());
Serial.print(F("/"));
Serial.print(gps.date.year());
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" "));
if (gps.time.isValid())
{
if (gps.time.hour() < 10) Serial.print(F("0"));
Serial.print(gps.time.hour());
Serial.print(F(":"));
if (gps.time.minute() < 10) Serial.print(F("0"));
Serial.print(gps.time.minute());
Serial.print(F(":"));
if (gps.time.second() < 10) Serial.print(F("0"));
Serial.print(gps.time.second());
Serial.print(F("."));
if (gps.time.centisecond() < 10) Serial.print(F("0"));
Serial.print(gps.time.centisecond());
}
else
{
Serial.print(F("INVALID"));
}
Serial.println();
}
Также можно получить эти данные без сторонних библиотек
Для определения положения и времени нам потребуется строка начинающиеся c $GNRMC
или $GPRMC
, дальше парсим значения из представленной выше таблицы и преобразуем их в нужные вам величины.
#ifndef HAVE_HWSERIAL1
#include <SoftwareSerial.h>
SoftwareSerial Serial1(2, 3); // RX, TX
#endif
#define DEBUG false
void setup() {
Serial.begin(115200);
Serial1.begin(9600);
Serial.println("Waiting connect...");
}
void loop() {
read();
}
bool isWaiting(String RMCdata) {
String stat = getChunkValue(RMCdata, ',', 2);
if (stat == "A") {
return false;
}
return true;
}
String getDateFromRMC(String RMCdata) {
String dateData = getChunkValue(RMCdata, ',', 9);
if (dateData.length() > 0) {
String dayStr = dateData.substring(0, 2);
String monthStr = dateData.substring(2, 4);
String yearStr = dateData.substring(4, 6);
return dayStr + "." + monthStr + "." + yearStr;
}
return "Waiting...";
}
String getTimeFromRMC(String RMCdata) {
String timeData = getChunkValue(RMCdata, ',', 1);
if (timeData.length() > 0) {
String hours = timeData.substring(0, 2);
String minutes = timeData.substring(2, 4);
String seconds = timeData.substring(4, 6);
return hours + ":" + minutes + ":" + seconds;
}
return "Waiting...";
}
String getLatFromRMC(String RMCdata) {
String lat = getChunkValue(RMCdata, ',', 3);
String indicator = getChunkValue(RMCdata, ',', 4);
return getPosition(lat, indicator);
}
String getLonFromRMC(String RMCdata) {
String lon = getChunkValue(RMCdata, ',', 5);
String indicator = getChunkValue(RMCdata, ',', 6);
return getPosition(lon, indicator);
}
int getSpeedFromRMC(String RMCdata) {
String speedData = getChunkValue(RMCdata, ',', 7);
return int(speedData.toFloat() / float(0.53995680346));
}
String getPosition(String data, String indicator) {
int D = int(data.toFloat() / float(100));
float M = data.toFloat() - float(D) * float(100);
float pos = D + M/60;
if (indicator == "S" or indicator == "W") {
pos = pos * -1;
}
return String(pos, 6);
}
String getChunkValue(String data, char separator, int index) {
int maxIndex = data.length() - 1;
int j = 0;
String chunkVal = "";
for (int i = 0; i <= maxIndex && j <= index; i++) {
chunkVal.concat(data[i]);
if (data[i] == separator) {
j++;
if (j > index) {
chunkVal.trim();
return chunkVal.substring(0, chunkVal.length() - 1);
}
chunkVal = "";
}
else if ((i == maxIndex) && (j < index)) {
chunkVal = "";
return chunkVal;
}
}
}
void read() {
String RMC;
String ReadString;
ReadString=Serial1.readStringUntil('\r');
ReadString.trim();
if (DEBUG) {
Serial.println(ReadString);
}
if (ReadString.startsWith("$GPRMC") or ReadString.startsWith("$GNRMC")) {
RMC = ReadString;
if (!isWaiting(RMC)) {
Serial.println("+++ GPS Data +++");
Serial.println("Date UTC = " + getDateFromRMC(RMC));
Serial.println("Time UTC = " + getTimeFromRMC(RMC));
Serial.println("Lat = " + getLatFromRMC(RMC));
Serial.println("Lon = " + getLonFromRMC(RMC));
Serial.println("Speed km/h = " + String(getSpeedFromRMC(RMC)) );
Serial.println("++++++++++++++++");
}
}
}