Подключение GPS модуля к Arduino

Рассказывать о том, что такое 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)

a-mega-gps-h450.png

Схема подключения для Arduino Uno

GPS VCC -> UNO PIN 5V или 3.3V
GPS GND -> UNO PIN GND
GPS TX -> UNO PIN 2
GPS RX -> UNO PIN 3

a-uno-gps-h450.png

Просмотр присылаемых данных с модуля

Время установки соединения со спутниками при первом запуске может занимать до 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());
  }
}

Screenshot_arduino_serial_port.png

Подробно о данных которые мы получили.

Модуль нам присылает данные в виде 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("++++++++++++++++");
    }
  }
}