====== GPS-телеметрия для любительского картинга ====== В любительском картинге всегда остро стоял вопрос объективного контроля тренировок. Профессионалам-то легко, с ними работает целая команда техников, инженеров, тренеров. Каждое движение руля и педалей фиксируется с миллиметровой точностью. Все ошибки чётко видны на телеметрии. А что делать прокатчику, если хочется не просто «бомбить» трассу, а работать над улучшением результатов? Из точных данных — лишь распечатка по итогам заездов. Кто-то снимает свои круги на видео, кто-то пользуется фитнес-трекерами и друзьями с секундомерами. {{ :projects:racing-gps-tracker:gps-imu-tracker.jpg |}} Мы решили сделать свою — простую и бюджетную — телеметрию. За основу взяли GPS-модуль и скрестили его с акселерометром. Данные пишутся на SD-карту в формате, который напрямую импортируется на Google Maps и Яндекс.Карты. * Платформы: Arduino Leonardo, Iskra Neo * Языки программирования: Arduino (C++) * Теги: GPS, GLONASS, MP3, IMU, телеметрия, автогонки ===== Видеообзор ===== {{youtube>m9vcIC1_z3k?large}} ===== Что это ===== ==== Базовое устройство ==== Простое устройство, повторяющее мини-проект «[[/slot-box:gps-tracker|GPS-трекера]]». Координаты и скорость определяются [[amp>product/troyka-gps-glonass?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|приёмником спутниковой навигации]] и вместе с данными [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|акселерометра]] записываются на [[amp>product/microsd-16gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|microSD]] с помощью [[amp>product/troyka-sd?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|картридера]]. Запись трека включается и выключается [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|светодиодной кнопкой]]. {{ :projects:racing-gps-tracker:yandex.n.maps.jpg |}} Данные сохраняются в электронную таблицу dataGPS.csv, формат которой соответствует требованиям сервиса «[[https://n.maps.yandex.ru/|Яндекс. Народная карта]]». === Что понадобится === - [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]] - [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]] - [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]] - [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|акселерометр]] - [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|светодиодная кнопка]] - [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]] - [[amp>product/microsd-16gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|карта microSD]] - [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]] === Программный код === // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // библиотека для работы с устройствами по SPI #include // библиотека для работы с SD-картой #include // библиотека для работы с GPS устройством #include // создаём объект класса GPS и передаём в него объект Serial1 GPS gps(Serial1); // создаём объект для работы с акселерометром Accelerometer accel; // пин светодиода #define LED_PIN A0 // пин кнопки #define BUTTON_PIN 13 // пин CS micro-sd карты #define SD_CHIP_SELECT_PIN 9 // интервал времени записи данных на карту #define INTERVAL 5000 // задаём размер массива для времени, даты, широты и долготы #define MAX_SIZE_MASS 16 // массив для хранения текущего времени char time[MAX_SIZE_MASS]; // состояние записи на карту памяти bool stateRec = false; // запоминаем текущее время long startMillis = millis(); // счётчк записи int counter = 0; void setup() { // устанавливаем светодиод в режим выхода pinMode(LED_PIN, OUTPUT); // устанавливаем кнопку в режим входа pinMode(BUTTON_PIN, INPUT_PULLUP); // открываем последовательный порт для мониторинга действий в программе Serial.begin(115200); // ждём, пока не откроется монитор последовательного порта // для того, чтобы отследить все события в программе while (!Serial) { } Serial.print("Serial init OK\r\n"); // открываем Serial-соединение с GPS-модулем Serial1.begin(115200); // выводим информацию об инициализации в Serial-порт Serial.println("Initializing SD card..."); // инициализируем SD-карту while (!SD.begin(SD_CHIP_SELECT_PIN)) { Serial.println("Card failed, or not present"); delay(1000); } // выводим информацию в Serial-порт Serial.println("Card initialized"); // выводим сообщение о начале инициализации акселерометра Serial.println("Initializing accelerometer..."); // инициализация акселерометра accel.begin(); // устанавливаем чувствительность акселерометра // 2g — по умолчанию, 4g, 8g accel.setRange(RANGE_2G); // выводим сообщение об удачной инициализации Serial.println("Accelerometer initialized"); } void loop() { // Фиксируем нажатие кнопки if (digitalRead(BUTTON_PIN)) { // меняем состояние «запись» / «не запись» на карту памяти stateRec = !stateRec; // меняем состояние светодиода индикации digitalWrite(LED_PIN, stateRec); } // если пришли данные с gps-модуля if (gps.available()) { // считываем данные и парсим gps.readParsing(); // считываем состояние GPS-модуля switch(gps.getState()) { // всё OK case GPS_OK: Serial.println("GPS is OK"); // если прошёл заданный интервал времени и запись данных включена if (millis() - startMillis > INTERVAL && stateRec) { // сохраняем данные GPS и акселерометра на карту памяти saveSD(); // запоминаем текущее время startMillis = millis(); } break; // ошибка данных case GPS_ERROR_DATA: Serial.println("GPS error data"); break; // нет соединение со спутниками case GPS_ERROR_SAT: Serial.println("GPS no connect to satellites"); break; } } } // функция сохарение данных на карту памяти void saveSD() { File dataFile = SD.open("dataGPS.csv", FILE_WRITE); // если файл существует и открылся if (dataFile) { // считывает текущее время gps.getTime(time, MAX_SIZE_MASS); // записываем время на карту памяти // считываем и записываем координаты широты и долготы на карту памяти dataFile.print(gps.getLatitudeBase10(), 6); dataFile.print("\t"); dataFile.print(gps.getLongitudeBase10(), 6); dataFile.print("\t"); dataFile.print(counter++); dataFile.print("\t"); dataFile.print(gps.getSpeedKm()); dataFile.print("km/h"); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.println(time); // записываем данные направления и величины ускорения в м/с² по оси X и Y dataFile.close(); Serial.println("Save OK"); } else { Serial.println("Error opening dataGPS.csv"); } } ==== Устройство в герметичном корпусе ==== Главный слоган данного девайса: «Безопасность и надёжность». За основу взят «GPS-трекер», с небольшими изменениями: * [[amp>product/krona-battery?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Крона]] заменена на [[amp>product/arduino-power-shield-li-ion?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Power Shield]] * [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]] заменён на [[amp>product/sealed-enclosure-115x90x80?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Герметичный корпус 115×90×80]] * [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Кнопка со светодиодом (Troyka-модуль)]] заменена на [[amp>product/troyka-touch-sensor?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Сенсорная кнопка (Troyka-модуль)]] и [[amp>product/troyka-led-module?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиод «Пиранья» (Troyka-модуль)]] Благодаря данным изменениям устройство сможет работать и в снег, и в дождь. === Что понадобится === - [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]] - [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]] - [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]] - [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Акселерометр]] - [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]] - [[amp>product/microsd-8gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Карта microSD]] - [[amp>product/troyka-touch-sensor?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Сенсорная кнопка (Troyka-модуль)]] - [[amp>product/troyka-led-module?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиод «Пиранья» (Troyka-модуль)]] - [[amp>product/sealed-enclosure-115x90x80?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Герметичный корпус 115×90×80]] === Программный код === // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // библиотека для работы с устройствами по SPI #include // библиотека для работы с SD-картой #include // библиотека для работы с GPS устройством #include // создаём объект класса GPS и передаём в него объект Serial1 GPS gps(Serial1); // создаём объект для работы с акселерометром Accelerometer accel; // пин светодиода #define LED_PIN A4 // пин кнопки #define BUTTON_PIN 8 // пин CS micro-sd карты #define SD_CHIP_SELECT_PIN 9 // интервал времени записи данных на карту #define INTERVAL 5000 // задаём размер массива для времени, даты, широты и долготы #define MAX_SIZE_MASS 16 // массив для хранения текущего времени char time[MAX_SIZE_MASS]; // состояние записи на карту памяти bool stateRec = false; // запоминаем текущее время long startMillis = millis(); // счётчк записи int counter = 0; void setup() { // устанавливаем светодиод в режим выхода pinMode(LED_PIN, OUTPUT); // устанавливаем кнопку в режим входа pinMode(BUTTON_PIN, INPUT_PULLUP); // открываем последовательный порт для мониторинга действий в программе Serial.begin(115200); // ждём, пока не откроется монитор последовательного порта // для того, чтобы отследить все события в программе while (!Serial) { } Serial.print("Serial init OK\r\n"); // открываем Serial-соединение с GPS-модулем Serial1.begin(115200); // выводим информацию об инициализации в Serial-порт Serial.println("Initializing SD card..."); // инициализируем SD-карту while (!SD.begin(SD_CHIP_SELECT_PIN)) { Serial.println("Card failed, or not present"); delay(1000); } // выводим информацию в Serial-порт Serial.println("Card initialized"); // выводим сообщение о начале инициализации акселерометра Serial.println("Initializing accelerometer..."); // инициализация акселерометра accel.begin(); // устанавливаем чувствительность акселерометра // 2g — по умолчанию, 4g, 8g accel.setRange(RANGE_2G); // выводим сообщение об удачной инициализации Serial.println("Accelerometer initialized"); } void loop() { // Фиксируем нажатие кнопки if (digitalRead(BUTTON_PIN)) { // меняем состояние «запись» / «не запись» на карту памяти stateRec = !stateRec; // меняем состояние светодиода индикации digitalWrite(LED_PIN, stateRec); } // если пришли данные с gps-модуля if (gps.available()) { // считываем данные и парсим gps.readParsing(); // считываем состояние GPS-модуля switch(gps.getState()) { // всё OK case GPS_OK: Serial.println("GPS is OK"); // если прошёл заданный интервал времени и запись данных включена if (millis() - startMillis > INTERVAL && stateRec) { // сохраняем данные GPS и акселерометра на карту памяти saveSD(); // запоминаем текущее время startMillis = millis(); } break; // ошибка данных case GPS_ERROR_DATA: Serial.println("GPS error data"); break; // нет соединение со спутниками case GPS_ERROR_SAT: Serial.println("GPS no connect to satellites"); break; } } } // функция сохарение данных на карту памяти void saveSD() { File dataFile = SD.open("dataGPS.csv", FILE_WRITE); // если файл существует и открылся if (dataFile) { // считывает текущее время gps.getTime(time, MAX_SIZE_MASS); // записываем время на карту памяти // считываем и записываем координаты широты и долготы на карту памяти dataFile.print(gps.getLatitudeBase10(), 6); dataFile.print("\t"); dataFile.print(gps.getLongitudeBase10(), 6); dataFile.print("\t"); dataFile.print(counter++); dataFile.print("\t"); dataFile.print(gps.getSpeedKm()); dataFile.print("km/h"); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.println(time); // записываем данные направления и величины ускорения в м/с² по оси X и Y dataFile.close(); Serial.println("Save OK"); } else { Serial.println("Error opening dataGPS.csv"); } } ==== Трекер с MP3-магнитолой ==== Мы пошли дальше и решили скреативить. Сделаем стильный музыкальный корпус устройства из #структора, детского железного конструктора и зальём всё это парой баллонов краски: * [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]] заменим на [[amp>product/structor-slot-box-xl?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box XL (#Структор)]] * [[amp>product/krona-battery?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Крона]] заменим на [[amp>product/arduino-power-shield-li-ion?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Power Shield]] * Добавим позитива с помощью [[amp>product/df-player-mp3-module?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | MP3-плеера]] === Что понадобится === - [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]] - [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]] - [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]] - [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Акселерометр]] - [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиодная кнопка]] - [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]] - [[amp>product/df-player-mp3-module?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | MP3-плеер]] - [[amp>product/microsd-8gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Карта microSD]] 2 шт. - [[amp>product/stackable-pin-headers-with-icsp?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Контактные колодки Arduino с проставкой под ICSP]] 2 шт. - [[amp>product/structor-slot-box-xl?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box XL (#Структор)]] === Программный код === // библиотека программного Serial-порта #include // библиотека для работы с mp3 плеером #include // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // библиотека для работы с устройствами по SPI #include // библиотека для работы с SD-картой #include // библиотека для работы с GPS устройством #include // создаём объект класса GPS и передаём в него объект Serial1 GPS gps(Serial1); // создаём объект для работы с акселерометром Accelerometer accel; // инициализируем новый последовательный порт // RX 10, TX 11 SoftwareSerial mp3Serial(10, 11); // пин воспрозведение музыки #define PLAY_PIN 7 // пин увеличение громкости #define VOLUME_UP_PIN A5 // пин уменьшение громкости #define VOLUME_DOWN_PIN A4 // пин светодиода #define LED_PIN A0 // пин кнопки #define BUTTON_PIN 13 // пин CS micro-sd карты #define SD_CHIP_SELECT_PIN 9 // интервал времени записи данных на карту #define INTERVAL 5000 // задаём размер массива для времени, даты, широты и долготы #define MAX_SIZE_MASS 16 // массив для хранения текущего времени char time[MAX_SIZE_MASS]; // состояние записи на карту памяти bool stateRec = false; // запоминаем текущее время long startMillis = millis(); // счётчк записи int counter = 0; // уровень громкости плеера int volume = 15; void setup() { // устанавливаем светодиод в режим выхода pinMode(LED_PIN, OUTPUT); // устанавливаем кнопку в режим входа pinMode(BUTTON_PIN, INPUT_PULLUP); // открываем последовательный порт для мониторинга действий в программе Serial.begin(115200); // ждём, пока не откроется монитор последовательного порта // для того, чтобы отследить все события в программе while (!Serial) { } Serial.print("Serial init OK\r\n"); // открываем Serial-соединение с GPS-модулем Serial1.begin(115200); // выводим информацию об инициализации в Serial-порт Serial.println("Initializing SD card..."); // инициализируем SD-карту while (!SD.begin(SD_CHIP_SELECT_PIN)) { Serial.println("Card failed, or not present"); delay(1000); } // выводим информацию в Serial-порт Serial.println("Card initialized"); // выводим сообщение о начале инициализации акселерометра Serial.println("Initializing accelerometer..."); // инициализация акселерометра accel.begin(); // устанавливаем чувствительность акселерометра // 2g — по умолчанию, 4g, 8g accel.setRange(RANGE_2G); // выводим сообщение об удачной инициализации Serial.println("Accelerometer initialized"); // открываем программный Serial-порт mp3Serial.begin(9600); // выбор Serial для упрапвления mp3-плеером mp3_set_serial(mp3Serial); // установка громкости mp3_set_volume(volume); } void loop() { // если нажата кнопка воспроизведение музыки if (!digitalRead(PLAY_PIN)) { mp3_play(); delay(100); } // если нажата кнопка увеличение громкости if (!digitalRead(VOLUME_UP_PIN)) { volume = volume + 5; delay(50); mp3_set_volume(volume); } // если нажата кнопка уменьшение громкости if (!digitalRead(VOLUME_DOWN_PIN)) { volume = volume - 5; delay(50); mp3_set_volume(volume); } // Фиксируем нажатие кнопки if (digitalRead(BUTTON_PIN)) { // меняем состояние «запись» / «не запись» на карту памяти stateRec = !stateRec; // меняем состояние светодиода индикации digitalWrite(LED_PIN, stateRec); } // если пришли данные с gps-модуля if (gps.available()) { // считываем данные и парсим gps.readParsing(); // считываем состояние GPS-модуля switch(gps.getState()) { // всё OK case GPS_OK: Serial.println("GPS is OK"); // если прошёл заданный интервал времени и запись данных включена if (millis() - startMillis > INTERVAL && stateRec) { // сохраняем данные GPS и акселерометра на карту памяти saveSD(); // запоминаем текущее время startMillis = millis(); } break; // ошибка данных case GPS_ERROR_DATA: Serial.println("GPS error data"); break; // нет соединение со спутниками case GPS_ERROR_SAT: Serial.println("GPS no connect to satellites"); break; } } } // функция сохарение данных на карту памяти void saveSD() { File dataFile = SD.open("dataGPS.csv", FILE_WRITE); // если файл существует и открылся if (dataFile) { // считывает текущее время gps.getTime(time, MAX_SIZE_MASS); // записываем время на карту памяти // считываем и записываем координаты широты и долготы на карту памяти dataFile.print(gps.getLatitudeBase10(), 6); dataFile.print("\t"); dataFile.print(gps.getLongitudeBase10(), 6); dataFile.print("\t"); dataFile.print(counter++); dataFile.print("\t"); dataFile.print(gps.getSpeedKm()); dataFile.print("km/h"); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.print(accel.readAX()); dataFile.print("\t"); dataFile.println(time); // записываем данные направления и величины ускорения в м/с² по оси X и Y dataFile.close(); Serial.println("Save OK"); } else { Serial.println("Error opening dataGPS.csv"); } } ===== Что дальше ===== Добавьте к устройству [[amp>product/troyka-imu-10-dof?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|IMU-модуль на 10 степеней свободы]] и соберите настоящий «чёрный ящик» для легкомоторного самолёта. Подключите [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|GPRS Shield]] и соберите автономную сигнализацию для мотоцикла или велосипеда. Черпайте вдохновение для новых устройств в подборке [[slot-box:start|мини-проектов для Arduino и Iskra Neo]].