Блок с датойБлок с временемБлок с возрастом сайта
Mr.ALB

    Анатолий Беляев (aka Mr.ALB). Персональный сайт

    Да пребудут с вами Силы СВЕТА!

     

    Ардуино (Arduino). #16

    GPS-модуль GY-NEO6MV2

    Купил на алиэкспрессе GPS-модуль GY-NEO6MV2, с помощью которого можно определить координаты местоположения. Интересно для всяких рыбаков, лесников, грибников и тех, кто не может с помощью телефона определить свои координаты, или находится там, где нет сотовых телефонных станций.

    GPS-модуль GY-NEO6MV2
    GPS-модуль GY-NEO6MV2

    Подразделы


     

    Описание

    GPS является аббревиатурой, т.е. расшифровывается как: Global Position System. GPS-модуль предназначен для получения сигналов позиционирования со спутников, которые летают над Землёй. Связь, само-собой, осуществляется в одном направлении – на приём. Поэтому при подключении модуля к Ардуино достаточно подключить вывод Tx модуля с Rx Ардуино. Так как само Ардуино использует последовательный порт Rx/Tx для связи с компьютером, то необходимо для GPS-модуля создать последовательный порт, который бы использовал иные контакты, отличные от 0(Rx)/ 1(Tx). Для этого можно воспользоваться функцией библиотеки SoftwareSerial. Используя эту библиотеку можно назначить любые порты под Rx/Tx. В моей программе они назначены на RXPin = 4, TXPin = 3. Скорость передачи данных с модуля по умолчанию: 9600 бод. Поэтому в скетче устанавливаем такое же значение при инициализации программного последовательного порта.

    По последовательному порту с приёмника GPS-модуля поступают текстовые символы, поля которых отделены запятыми. В конце строки стоит завершающий символ перевода строки. Формат сообщений соответствует стандарту NMEANational Marine Electronics Association. Несколько подробнее этот формат описан ниже в начале скетча. Отмечу лишь, что посылка с основными данными начинается с кодового слова $GPRMC, поэтому декодировать будем ту сторку, которая имеет вначале это слово.

    Для вывода информации, полученной с GPS-модуля, использую небольшой OLED дисплей под управлением контроллера SSD1306. Почему OLED дисплей? Потому, что его можно считывать при дневном освещении, что затруднительно с TFT дисплеем.

     

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

    Схема подключения GPS-модуля GY-NEO6MV2 к Ардуино простейшее: Питание на модуль на контакт VCC подавал и +5 В, и +3.3 В, какой-либо разницы не заметил, в обоих случаях модуль выдаёт необходимую информацию. Сам контроллер GPS-модуля работает от +3.3 В, на плате модуля есть стабилизатор на это напряжение, поэтому модуль можно запитывать напрямую от +5 В Ардуино – удобно, если будет использоваться Arduino Pro Mini, у которой на плате нет стабилизатора на +3.3 В.

    Выходная информация снимается с контакта Tx модуля, его подключаем к программному последовательному порту Ардуино. В моём скетче это цифровой контакт 4 Ардуино.

    Контакт Rx модуля не используется.

    Кому интересно, то схема самого модуля ниже.

    Схема GPS-модуля GY-NEO6MV2
    Pic 1. Схема GPS-модуля GY-NEO6MV2

     

    Скетч

    Ниже представлен скетч считывания информации с GPS-модуля GY-NEO6MV2. Файл со скетчем можно скачать в подразделе Приложение.

    Были проблемы с выводом на OLED дисплей информации типа string. Поэтому вывожу координаты в том формате, в котором их выдаёт сам модуль, то есть значение широты и долготы необходимо дополнительно разделить на 100, тогда получим значение в градусах и минутах разделённых десятичной точкой.

    Дополнительно кроме широты и долготы можно выводить: скорость, дату и точное время. Время вывожу с точностью до секунды, хотя модуль принимает очень точное время с точностью до тысячной секунды.

    Вывод информации на OLED SSD1306
    Pic 2. Вывод информации на OLED SSD1306
    /******************************************************
     * Информация по декодированию посылки с GPS-module
     * 
      Формат сообщений соответствует стандарту национальной
      ассоциации морской электроники NMEA
      (National Marine Electronics Association). 
      Сообщение — это текстовая строка, завершающаяся
      символом перевода строки, с полями,
      разделенными запятыми.
      Далее показано, как выглядит типичное сообщение:
      $GPRMC,081019.548,A,5342.6316,
        N,00239.8728,W,000.0,079.7,110613,,,A*76
      Поля в данном примере имеют следующие значения:
      • 0 $GPRMC — тип сообщения;
      • 1 081019.548 — время (очень точное)
          в 24-часовом формате, 8:10:19.548;
      • 2 A — метка готовности A - ok, V - недействительно
      • 3,4 5342.6316, N — широта, умноженная на 100,
          то есть 53,426316 градуса северной широты;
      • 5,6 00239.8728,W — долгота, умноженная на 100,
          то есть 0,2398728 градуса западной долготы;
      • 7 000.0 — скорость;
      • 8 079.7 — курс 79,7 градуса;
      • 9 110613 — дата, 11 июня 2013.
      Остальные поля для данного примера не имеют значения.
    
      ПРИМЕЧАНИЕ
      Полный список сообщений NMEA GPS можно найти 
      по адресу http://aprs.gids.nl/nmea/
    
        $ GPBOD - пеленг, исходная точка к пункту назначения
        $ GPBWC - пеленг и расстояние до маршрутной точки,
            большой круг
        $ GPGGA - данные о фиксации глобальной 
            системы позиционирования
        $ GPGLL - географическое положение,
            широта / долгота
        $ GPGSA - GPS DOP и активные спутники
        $ GPGSV - спутники GPS в вид
        $ GPHDT - товарную, True
        $ GPR00 - Список путевых точек
            в настоящее время активный маршрут
        $ GPRMA - Рекомендуемый минимальный
            удельный Loran-C данные
        $ GPRMB - Рекомендуемая минимальная
            информация навигации
        $ GPRMC - Рекомендуемый минимальный
            удельный GPS / Транзитные данные
        $ GPRTE - Маршруты
        $ GPTRF - Данные исправления транзита
        $ GPSTN - Несколько идентификаторов данных
        $ GPVBW - Двойная скорость
            относительно земли / воды
        $ GPVTG - Трек и скорость относительно земли
        $ GPWPL - Местоположение маршрутной точки
        $ GPXTE - Поперечная ошибка, измеренная
        $ GPZDA - Дата и время
        
      Существует полный список $GPxxx кодов
      членов предложений, доступных,
      без ссылок на деталь формата.
      
      $GPRMC, hhmmss.ss, A, llll.ll, a, yyyyy.yy,
        a, xx, xx, ddmmyy, xx, a * hh
      1 = UTC время ччммсс.сс
      2 = статус данных А = норма 
        (V = предупреждение навигационного приемника)
      3 = Широта
      4 = N или S
      5 = долгота
      6 = E или W
      7 = скорость относительно земли в узлах
      8 = Трасса исправлена ​​в градусах Истина
      9 = дата UT
      10 = Степени магнитного отклонения 
        (восточная вариация вычитается из истинного курса)
      11 = E или W
      12 = Контрольная сумма
    
      $GPZDA - Дата и время
    
      метка, UTC, день, месяц, год и местный часовой пояс.
      $GPZDA, hhmmss.ss, xx, xx, xxxx, xx, xx
      1 = hhmmss.ss = UTC
      2 = xx = день, с 01 по 31
      3 = xx = месяц, с 01 по 12
      4 = xxxx = год
      5 = xx = описание локальной зоны, от 00 до + / - 13 часов
      6 = xx = Описание минут 
        местной зоны (тот же знак, что и часы)
    */
    
    /***********************************************************
      http://www.mralb.ru/sections/programming/arduino_16.php
      2020-07-29 Mr.ALB Тренировка в программировании Ардуино
      2020-11-11 Mr.ALB Проверка на OLED SSD 0.96 inch 128*64
      2020-11-12 Mr.ALB Модуль GPS NEO-6M
      2020-11-15 Mr.ALB Переделка всей программы и функций
      2020-12-01 Mr.ALB Выборочный вывод доп.строк
      
       Arduino UNO   NEO-6M
        +3.3V/5V      VCC
        GND           GND
        pin4          TX
        -             RX - не используется
    
        Программное подключение дисплея по SPI
        ARDUINO  R       OLED SSD 0.96' 128*64
        pin9   - R=1k  - SCL
        pin10  - R=1k  - SDA
        pin8   - R=1k  - RST | RES
        pin11  - R=1k  - DC
        pin12  - R=1k  - CS
        +3.3V          - VCC
        GND            - GND
     ****************************************************/
    #include <SPI.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>
    
    // OLED display width, in pixels
    #define SCREEN_WIDTH 128
    // OLED display height, in pixels
    #define SCREEN_HEIGHT 64
    
    // Declaration for SSD1306 display
    // connected using software SPI:
    // Программный SPI:
    #define OLED_RESET 8
    #define OLED_SCK   9
    #define OLED_SDA   10
    #define OLED_DC    11
    #define OLED_CS    12
    
    // Создаём объект дисплея oled
    //  Adafruit_SSD1306(uint8_t w, uint8_t h,
    //        int8_t mosi_pin,int8_t sclk_pin,
    //        int8_t dc_pin,int8_t rst_pin,
    //        int8_t cs_pin);
    
    Adafruit_SSD1306 oled
    (
      SCREEN_WIDTH,
      SCREEN_HEIGHT,
      OLED_SDA,
      OLED_SCK,
      OLED_DC,
      OLED_RESET,
      OLED_CS
    );
    
    const uint8_t znak_w = 6, // Ширина знака в пикселях
                  znak_h = 8; // Высота знака в пикселях
    const uint8_t pos_x = 31, // Позиция параметра 31 пикс
                  // Позиция размерности
                  pos_x_d = pos_x + 72,
                  // Строка широты
                  pos_y_lat = znak_h * 2 + 2,
                  // Строка долготы
                  pos_y_long = pos_y_lat + znak_h + 1;
                  
    uint8_t pos_y_spd = pos_y_long + znak_h + 1, // Скорость
            pos_y_date = pos_y_spd + znak_h + 1, // Строка даты
            pos_y_time = pos_y_date + znak_h + 1;// Строка времени
    
    // Поясное время: Самарское UTC+4
    const int time_offset = 4;
    
    // Массив для считанной строки с модуля GPS
    const int sentenceSize = 80;// Размер строки
    char sentence[sentenceSize];// Массив под строку
    
    // Массив строк для заставки
    const char *zastav[] =
    {
      "GPS",
      "-module",
      "Mr.ALB",
      "v1.2",
      "2020-12-01"
    };
    
    // Массив строк основных надписей
    const char *text[] =
    {
      "Lat:",
      "Lng:",
      "Spd:",
      "Date:",
      "Time:"
    };
    
    // Программный серийный порт
    #include <SoftwareSerial.h> 
    
    // TXpin-не используется
    static const int RXPin = 4, TXPin = 3;  
    static const uint32_t GPSBaud = 9600;
    
    // Программный серийный(последовательный) порт для GPS device
    SoftwareSerial ss(RXPin, TXPin);
    
    // Флаги выбора дополнительных строк
    bool flag_spd = false;  // Скорость
    bool flag_date = true;  // Дата
    bool flag_time = true;  // Время
    
    /**********************
       Настройка программы
     **********************/
    void setup()
    {
      Serial.begin(9600);
      // Подключение дисплея
      if (!oled.begin(SSD1306_SWITCHCAPVCC))
      {
        Serial.println(F("SSD1306 allocation failed"));
        while (true); // Ошибка дисплея. Останов программы
      }
    
      // Старт программного серийного порта для считывания с GPS
      ss.begin(GPSBaud);
    
      // Заставка
      oled.clearDisplay();
      fnStrPrintOLED(30, 0, zastav[0], 2, WHITE, false);
      fnStrPrintOLED(68, 0, zastav[1], 1, WHITE, false);
      fnStrPrintOLED(46, znak_h * 3, zastav[2], 1, WHITE, false);
      fnStrPrintOLED(52, znak_h * 4 + 3, zastav[3], 1, WHITE, false);
      fnStrPrintOLED(36, znak_h * 5 + 6, zastav[4], 1, WHITE, true);
    
      oled.display(); // Вывод на экран
      delay(3000);
    
      // Очистка буфера
      oled.clearDisplay();
    
      // Вывод заголовка
      fnStrPrintOLED(0, 0, zastav[3], 1, WHITE, false);
      fnStrPrintOLED(30, 0, zastav[0], 2, WHITE, false);
      fnStrPrintOLED(68, 0, zastav[1], 1, WHITE, true);
    
      // Вывод основных надписей
      fnStrPrintOLED(0, pos_y_lat, text[0], 1, WHITE, false);
      fnStrPrintOLED(0, pos_y_long, text[1], 1, WHITE, false);
      
      if (flag_spd)
      {
        fnStrPrintOLED(0, pos_y_spd, text[2], 1, WHITE, false);
      }
      else
      { // Если скорость не выводим,
        // то дату и время выводим выше на строчку
        pos_y_date -= znak_h;
        pos_y_time -= znak_h;
      }
      
      if (flag_date)
      {
        fnStrPrintOLED(0, pos_y_date, text[3], 1, WHITE, false);
      }
      else
      { // Если дату не выводим,
        // то время выводим выше на строчку
        pos_y_time -= znak_h;
      }
      
      if (flag_time)
      {
        fnStrPrintOLED(0, pos_y_time, text[4], 1, WHITE, true);
      }
    }
    
    /*********************
       Основная программа
     ********************/
    void loop()
    {
      static int i = 0;
      char ch = ' ';
      while (ss.available() && ch != '\n' && i < sentenceSize)
      {
        //Читаем посимвольно с программного последовательного порта
        char ch = ss.read();
        if (ch == '$' && i == 0)
        {
          sentence[i] = ch;
          i++;
        }
        else
        {
          if (sentence[0] = '$')
          {
            sentence[i] = ch;
            i++;
          }
        }
      }
      sentence[i] = '\0';
      i = 0;
      Serial.println(sentence);
      displayGPS(); // Вывод параметров
      delay(300);
    }
    
    /***************************************
        Функция вывода значений с модуля GPS
        2020-11-12 Mr.ALB
    */
    void displayGPS()
    {
      char field[12];// Буфер под значение поля
      String str;
    
      getField(field, 0); // Нулевое поле с меткой
      if (strcmp(field, "$GPRMC") == 0)
      {
        // Вывод значения широты
        getField(field, 3);
        // Очистка места под новое значение
        fnClearPlace(pos_x, pos_y_lat, SCREEN_WIDTH - pos_x - 1);
        fnStrPrintOLED(pos_x, pos_y_lat, field, 1, WHITE, false);
    
        // Размерность широты N/S
        getField(field, 4);
        str = field[0];
        fnClearPlace(pos_x_d, pos_y_lat, znak_w * 2);
        fnStrPrintOLED(pos_x_d, pos_y_lat, str, 1, WHITE, true);
    
        // Вывод значения долготы
        getField(field, 5);
        fnClearPlace(pos_x, pos_y_long, SCREEN_WIDTH - pos_x - 1);
        fnStrPrintOLED(pos_x, pos_y_long, field, 1, WHITE, false);
    
        // Размерность долготы E/W
        getField(field, 6);
        str = field[0];
        fnClearPlace(pos_x_d, pos_y_long, znak_w * 2);
        fnStrPrintOLED(pos_x_d, pos_y_long, str, 1, WHITE, true);
    
        // Вывод значения скорости
        if (flag_spd)
        {
          getField(field, 7);
          fnClearPlace(pos_x, pos_y_spd, SCREEN_WIDTH - pos_x - 1);
          fnStrPrintOLED(pos_x, pos_y_spd, field, 1, WHITE, true);
        }
    
        // Вывод даты
        if (flag_date)
        {
          getField(field, 9);
          str = field[0];
          str += field[1];
          // Очистка места под новое значение
          fnClearPlace(pos_x, pos_y_date, znak_w * 10);
          fnStrPrintOLED(pos_x, pos_y_date, str, 1, WHITE, false);
          fnStrPrintOLED(pos_x + 12, pos_y_date, "-", 1, WHITE, false);
          str = field[2];
          str += field[3];
          fnStrPrintOLED(pos_x + 18, pos_y_date, str, 1, WHITE, false);
          fnStrPrintOLED(pos_x + 30, pos_y_date, "-20", 1, WHITE, false);
          str = field[4];
          str += field[5];
          fnStrPrintOLED(pos_x + 48, pos_y_date, str, 1, WHITE, true);
        }
    
        // Вывод значения времени
        if (flag_time)
        {
          getField(field, 1);
          // Очистка места под новое значение
          fnClearPlace(pos_x, pos_y_time, znak_w * 10);
    
          str = field[0];
          str += field[1];
          int ihour = str.toInt() + time_offset;
          if (ihour < 10)str = "0";
          else str = "";
          str += String(ihour);
          fnStrPrintOLED(pos_x, pos_y_time, str, 1, WHITE, false);
          str = field[2];
          str += field[3];
    
          fnStrPrintOLED(pos_x + 12, pos_y_time, ":", 1, WHITE, false);
          fnStrPrintOLED(pos_x + 18, pos_y_time, str, 1, WHITE, false);
    
          str = field[4];
          str += field[5];
          fnStrPrintOLED(pos_x + 30, pos_y_time, ":", 1, WHITE, false);
          fnStrPrintOLED(pos_x + 36, pos_y_time, str, 1, WHITE, true);
        }
      }
    }
    
    /*******************************************************
       Функция получения поля из массива в буффер по индексу
    
       buffer - указатель на массив
       index  - номер необходимого поля
    
       2020-11-15 Mr.ALB
    */
    void getField(char* buffer, int index)
    {
      int sentencePos = 0;
      int fieldPos = 0;
      int fieldCount = 0;
    
      for (sentencePos = 0; sentencePos < sentenceSize; sentencePos++)
      {
        if (fieldCount == index)
        {
          if (sentence[sentencePos] == ',')break;
          // Заполнение поля
          buffer[fieldPos] = sentence[sentencePos];
          fieldPos ++;
        }
        if (fieldCount < index)
        { // Поиск заданного поля
          if (sentence[sentencePos] == ',')fieldCount++;
        }
      }
      buffer[fieldPos] = '\0';// Завершающий символ
    }
    
    /*************************************************
       Функция вывода строкового параметра на дисплее
       2020-07-29 Mr.ALB начальная
       2020-11-12 Mr.ALB переделка
       2020-11-15 Mr.ALB добавлен flag_display
    */
    void fnStrPrintOLED
    (
      byte   x,   //x-параметра
      byte   y,   //y-параметра
      String str, //строка-параметр
      byte   fsp, //font size-параметра
      int    color,//Цвет WHITE=on|BLACK=off
      bool   flag_display //Вывод на экран
    )
    {
      oled.setCursor(x, y);
      oled.setTextSize(fsp);
      oled.setTextColor(color);
      oled.print(str);
      // Вывод на экран
      if (flag_display)oled.display();
    }
    
    /********************************************
        Функция стирания места под новое значение
        Закрашиваем чёрным прямоугольником
        2020-11-12 Mr.ALB
    */
    void fnClearPlace(byte x, byte y, byte w)
    {
      oled.fillRect(x, y, w, znak_h, BLACK);
    }
    
    

     

    Реализация

    Для проверки работоспособности GPS-модуля использовал Arduino UNO R3. Можно использовать любое, только для дисплея необходимо напряжение +3.3 В, его можно получить через стабилизатор или подключать контакт VCC дисплея через сопротивление около 470 Ом на +5 В. Контакты дисплея используемые для SPI подключены через сопротивления 1.2 кОм.

    Проверка работы GPS-модуля
    Pic 3. Проверка работы GPS-модуля

    Чтобы устранить помехи, подключил параллельно питанию Ардуино два конденсатора: на 0.1 мкФ, и на 1000 мкФ 6.3 В.

    Дополнительные конденсаторы
    Pic 4. Дополнительные конденсаторы

    Этот проект чисто пробный, проверить работоспособность модуля. Какую-то конструкцию не делаю, т.к. до законченности требуется добавить несколько кнопок настройки, чтобы, к примеру, можно было бы устанавливать поясное время. В этом скетче стоит поясное время Самары UTC+4, себе установите Ваше поясное время.

    В заключение хочу сказать, что на GPS-модуле GY-NEO6MV2 есть красный светодиодик. Если он не светится и не мигает, то значит ваш модуль не обнаружил достаточное количество спутников и данные поступают неверные. Ещё может быть, что батарейка хранения данных в EEPROM модуля разрядилась, требуется какое-то время, пока она не подзарядится. Проверять работу модуля лучше всего на открытом пространстве или, на крайний случай, на подоконнике или балконе. Если у вас есть USB удлиннитель, то можно подключить к компьютеру всё устройство и через монитор последовательного порта смотреть какие посылки принимет ваш GPS-модуль. Будет более понятно.

    Заметил у себя, что когда недостаточно спутников, то в полсылке поля с координатами пустые, а вот время и дата заполнены, поэтому на дисплей выводится только дата и время.

    2020-12-01
    Анатолий Беляев.

     

    Приложение

    Используемые библиотеки и программы:


    . Mr.ALB
    Предыдущая страница Страница 17