Купил на алиэкспрессе 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-модуля поступают текстовые символы, поля которых отделены запятыми. В конце строки стоит завершающий символ перевода строки. Формат сообщений соответствует стандарту NMEA – National 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. Файл со скетчем можно скачать в подразделе Приложение.
Были проблемы с выводом на OLED дисплей информации типа string. Поэтому вывожу координаты в том формате, в котором их выдаёт сам модуль, то есть значение широты и долготы необходимо дополнительно разделить на 100, тогда получим значение в градусах и минутах разделённых десятичной точкой.
Дополнительно кроме широты и долготы можно выводить: скорость, дату и точное время. Время вывожу с точностью до секунды, хотя модуль принимает очень точное время с точностью до тысячной секунды.
/****************************************************** * Информация по декодированию посылки с 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 кОм.
Чтобы устранить помехи, подключил параллельно питанию Ардуино два конденсатора: на 0.1 мкФ, и на 1000 мкФ 6.3 В.
Этот проект чисто пробный, проверить работоспособность модуля. Какую-то конструкцию не делаю, т.к. до законченности требуется добавить несколько кнопок настройки, чтобы, к примеру, можно было бы устанавливать поясное время. В этом скетче стоит поясное время Самары UTC+4, себе установите Ваше поясное время.
В заключение хочу сказать, что на GPS-модуле GY-NEO6MV2 есть красный светодиодик. Если он не светится и не мигает, то значит ваш модуль не обнаружил достаточное количество спутников и данные поступают неверные. Ещё может быть, что батарейка хранения данных в EEPROM модуля разрядилась, требуется какое-то время, пока она не подзарядится. Проверять работу модуля лучше всего на открытом пространстве или, на крайний случай, на подоконнике или балконе. Если у вас есть USB удлиннитель, то можно подключить к компьютеру всё устройство и через монитор последовательного порта смотреть какие посылки принимет ваш GPS-модуль. Будет более понятно.
Заметил у себя, что когда недостаточно спутников, то в полсылке поля с координатами пустые, а вот время и дата заполнены, поэтому на дисплей выводится только дата и время.
2020-12-01
Используемые библиотеки и программы: