Создаем GPS часы на Arduino

Добавлено 28 июня 2016 в 09:20

Вам необходим источник точного времени от GPS? Данная статья покажет вам, как использовать модуль GPS для получения времени, даты и координат, и как показать их на LCD индикаторе с помощью Arduino.

Что необходимо?

Введение

Создание системы глобального позиционирования, или GPS, началось в начале 1970-х годов. Каждая страна (Россия, США, Китай и т.д.) обладают своей собственной системой, но большинство средств спутниковой навигации в мире используют систему США.

Каждый спутник системы имеет атомные часы, которые непрерывно контролируются и корректируются NORAD (командованием воздушно-космической обороны Северной Америки) каждый день.

По сути, приемник по своим часам измеряет TOA (время получения сигнала, time of arrival) четырех спутниковых сигналов. Исходя из TOA и TOT (времени отправки сигнала, time of transmission), приемник вычисляет четыре значения времени «пролета» сигнала (TOF, time of flight), которые отличаются друг от друга в зависимости от расстояния спутник-приемник. Затем, исходя из четырех значений TOF, приемник вычисляет свое положение в трехмерном пространстве и отклонение своих часов.

Самые недорогие GPS приемники обладают точностью около 20 метров для большинства мест на Земле. Теперь посмотрим, как изготовить свои собственные часы GPS с помощью Arduino.

Аппаратная часть

Мой GPS модуль имеет 6 контактов: GND, Vin, Tx, Rx и снова GND. Шестой вывод никуда не подключен. Контакт GND соединен с корпусом на Arduino, Vin подключаем к шине +5В на Arduino, Tx подключен к выводу 10 на Arduino, а вывод Rx никуда не подключаем, так как не будем посылать на GPS модуль никаких сообщений. Мой модуль передает спутниковые данные, используя интерфейс RS-232, со скоростью 4800 бит/сек, которые принимаются Arduino на выводе 10.

Ниже показана фотография GPS модуля:

gps модуль
GPS модуль EM-411

Модуль отправляет то, что известно как NMEA сообщения. Здесь вы можете увидеть пример одного NMEA сообщения и его разъяснение (выдержка из технического описания):

$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,0000*18
Формат данных GGA
НазваниеПримерЕдиницыОписание
ID сообщения$GPGGA Заголовок протокола GGA
Время UTC161229.487 hhmmss.sss (две цифры часы, две цифры минуты, затем секунды с точностью до тысячных)
Широта3723.2475 ddmm.mmmm (первые две цифры градусы, затем минуты с точностью до десятитысячных)
Флаг N/SN N – север, S – юг
Долгота12158.3416 ddmm.mmmm (первые две цифры градусы, затем минуты с точностью до десятитысячных)
Флаг E/WW E – восток, W – запад
Индикатор местоположения1 
  • 0 – местоположение недоступно или некорректно;
  • 1 – режим GPS SPS, местоположение корректно;
  • 2 – дифференциальный GPS, режим SPS, местоположение корректно;
  • 3 – режим GPS PPS, местоположение корректно.
Количество используемых спутников07 В диапазоне от 0 до 12
HDOP1.0 Ухудшение точности по горизонтали
Высота относительно уровня моря9.0метры 
Единицы измеренияMметры 
Геоидальное различие  Различие между земным эллипсоидом WGS-84 и уровнем моря (геноидом)
Единицы измеренияMметры 
Возраст дифференциальных данных GPS секундыНулевые поля, когда DGPS не используется
ID станции, передающей дифференциальные поправки0000  
Контрольная сумма*18  
<CR><LF>  Конец сообщения

Все эти данные принимаются Arduino через вывод 10. Библиотека TinyGPS читает сообщения GPGGA и GPRMC (для подробной информации о GPRMC смотрите техническое описание).

Arduino на схеме не показан. Подключите периферийные устройства согласно подписанным соединениям.

схема gps часов на arduino
Схема GPS часов на arduino

Программное обеспечение

При подаче питания GPS модуль затрачивает некоторое время, чтобы получить правильное местоположения от спутников. Когда местоположение получено, модуль шлет NMEA сообщения на Arduino. Библиотека TinyGPS содержит функцию для получения времени и даты из GPRMC сообщения. Она называется crack_datetime() и принимает в качестве параметров семь указателей на переменные: год year, месяц month, день месяца day, часы hour, минуты minute, секунды second, и сотые доли секунды hundredths. Вызов функции выглядит так:

gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths);

Вызов данной функции возвращает вам в переменных правильные значения до тех пор, пока с железом всё в порядке.

Чтобы получить ваше местоположение, можно вызвать функцию f_get_position(). Данная функция принимает в качестве параметров два указателя на переменные: широта latitude и долгота longitude. Вызов данной функции выглядит так:

gps.f_get_position(&latitude, &longitude);

 Исходный текст программы:

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>

#define RXPIN 10
#define TXPIN 9
#define GPSBAUD 4800
#define RS 2
#define EN 3
#define D4 4
#define D5 5
#define D6 6
#define D7 7


TinyGPS gps;
SoftwareSerial uart_gps(RXPIN, TXPIN);
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// Переменные
int seconds;
int timeoffset = 1; // Пользователь должен изменить единицу на соответствующий часовой пояс. В примере используем сдвиг на +1 час.

// Объявление функций.
void getgps(TinyGPS &gps);

// Функция настройки - запускается только при включении
void setup()
{
  Serial.begin(115200);     // Запуск последовательного интерфейса для отладки
  uart_gps.begin(GPSBAUD);  // Запуск приемника UART для GPS
  lcd.begin(16,2);          // Объявление LCD 
  lcd.print("   GPS clock");   // Сообщение приветствия
  delay(1000);              // Ждем одну секунду
  lcd.clear();              // Очистить LCD
}

// Цикл главной программы - запущен всегда
void loop()
{
  while(uart_gps.available())
  {
    int c = uart_gps.read();
    if(gps.encode(c))
    {
      getgps(gps);
    }
  }
}

/*
 * Данная функция получает данные от GPS модуля
 * и отображает их на LCD
 */
  
void getgps(TinyGPS &gps)
{
int year;
float latitude, longitude;
byte month, day, hour, minute, second, hundredths;

  gps.f_get_position(&latitude, &longitude);
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths);
  
  hour = hour + timeoffset;

  lcd.clear();//lcd.setCursor(0, 0);
  lcd.print("Time: ");

  if (hour <= 9)
  {
    lcd.print("0"); lcd.print(hour, DEC);
  }
  else
  {
    lcd.print(hour, DEC);
  }
  lcd.print(":");
  if (minute <=9)
  {
    lcd.print("0"); lcd.print(minute, DEC);
  }
  else
  {
    lcd.print(minute, DEC); 
  }
  lcd.print(":");
  if (second <= 9) 
  {
    lcd.print("0"); lcd.print(second, DEC);
  } 
  else 
  {
    lcd.print(second, DEC);
  }
  lcd.setCursor(0,1);
  lcd.print("Date: ");
  if (day <= 9)
  {
    lcd.print("0"); lcd.print(day, DEC);
  }
  else
  {
    lcd.print(day, DEC);
  }
  lcd.print("-"); 
  if (month <= 9)
  {
    lcd.print(month, DEC); 
  }
  else
  {
    lcd.print(month, DEC);
  }
  lcd.print("-"); lcd.print(year, DEC);
  delay(2000);
  lcd.clear();
  lcd.print("Lat: "); lcd.print(latitude, DEC);
  lcd.setCursor(0,1);
  lcd.print("Lon: "); lcd.print(longitude, DEC);
  delay(2000);

// Debugging purpose only.
Serial.print(latitude, DEC); Serial.print(" - "); Serial.println(longitude, DEC);

}

Загрузки

Здесь все файлы, которые вам могут понадобиться:

Комплектующие: технические описания и ссылки на магазины (aliexpress и пр.)

Фото и видео

На фотографиях и видео я добавил смещение широты и долготы. Чтобы улучшить прием, я подключил GPS модуль через длинный кабель и высунул его в окно.

gps часы на arduino дата и время
Дата и время
gps часы на arduino широта и долгота
Широта и долгота
gps часы на arduino подключение
Подключение

Заключение

В данной статье мы рассмотрели, как получить дату, время и координаты от GPS модуля, а затем как их отобразить на LCD индикаторе. Теперь вы знаете, где находитесь!

Надеюсь статья оказалась полезной. Оставляйте комментарии!


На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


  • 2017-10-29Михаил Камин

    Всё это просто замечательно, но есть парочка нюансов:
    - поправку на поясное время сделали, но что будет показывать календарь в интервале от 00:00 до 03:00, например, московского времени? Вчерашнюю дату? И как быть с датами в конце-начале месяца (особенно в феврале, да ещё не забывая про високосный год)?
    - даже и с такой поправкой кода, MEGA будет сильно избыточна - более популярной ( и дешёвой) UNO или NANO - "за глаза".

  • 2017-10-02Lazy Fox

    Решил так:
    gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths);
    hour = hour + timeoffset;
    if (hour > 23) hour = hour - 24;
    Тогда время в часах будет в 24 часовом формате с учётом часового пояса.

  • 2017-10-01antzol

    При использовании SoftwareSerial основная проблема в задержке, которую добавляет этот программный порт, при вызове функций других библиотек (соответственно и других портов SoftwareSerial). На официальном сайте Arduino для получения непрерывных потоков данных с нескольких портов рекомендуют использовать дополнительно библиотеку AltSoftSerial.
    Здесь https://www.pjrc.com/teensy... , в конце статьи есть рекомендации по выбору скоростей при использовании одновременно трех uart портов (аппаратный, SoftwareSerial и AltSoftSerial).

  • 2017-10-01Lazy Fox

    Скорее наоборот, в Mega есть свободные аппаратные uart-порты и можно не использовать SoftwareSerial, а в Uno всего один, и тот нужен для прошивки и монитора порта. А вот скорости порта наверно сперва лучше выставить по дефолту 9600 (с завода в модуле gps), а потом при желании поднять/опустить. Тут если автор даст комментарий какая скорость и почему при использовании нескольких SoftwareSerial uart-портов предпочтительней, что бы не перегружать контроллер и не допускать переполнения буфера порта, - буду весьма благодарен

  • 2017-09-15Антон Кочерыгов

    На ардуино UNO чтобы создать что-то подобное, надо менять что-нибудь в коде и в схеме?

  • 2016-08-07zuka bash

    Очень понравилась статья, несколько дней пытался сделать похожее а здесь как угадали мои мысли.
    Широту-долготу и время показывает после инициализации модуля, с датой приходится подождать.
    Часовой пояс просто прибавляют и может появиться 25 часов, 26, и даже отрицательное.
    При выводе дня пропущена команда вывода "0" если меньше 10.

Сообщить об ошибке