GPS-спидометр и одометр

Проекты на Arduino и Slot Shield

В простых велокомпьютерах обычно считаются обороты колеса — зная количество оборотов в минуту не сложно подсчитать скорость и пройденное расстояние. Вот только датчики Холла на вилку ставить морока, они быстро покрываются грязью и начинают сбоить. Лишние провода отнюдь не украшают байк, а только привлекают внимание воришек. Да и что делать, если в хозяйстве не только велосипед, но ещё и самокат, ролики или сноуборд?

Мы пошли другим путём. Данные о скорости мы получаем от приёмника сигналов спутниковой навигации GLONASS/GPS. А дальше — дело техники. Милые сердцу моряков и авиаторов узлы мы переводим в привычные «км/ч», а информацию об изменении координат используем для определения пройденного расстояния. Полученные значения в реальном времени выводим на четырёхразрядный индикатор, переключаясь между выводом скорости и дистанции обычной тактовой кнопкой.

Получилось компактное устройство, которое быстро крепится на руль, раму или просто на руку. Без лишних проводов и проблем с калибровкой.

Что потребуется

Полный сет компонентов проекта. В сет входят:

Видеоинструкция

Как собрать

Установите Troyka Slot Shield на Iskra Neo

Поверните GLONASS/GPS на 90 градусов против часовой стрелки и вставьте в правый верхний слот.

Переверните Quad Dispaly вверх ногами и вставьте в центральный и правый слоты нижнего ряда.

Переверните тактовую кнопку на 90 градусов против часовой стрелки и вставьте в центральный верхний слот.

Скетч

Прошейте контроллер скетчем через Arduino IDE.

gps-speedometr.ino
#include "TroykaButton.h"
// создаём объект для работы с кнопкой
TroykaButton button(A2);
// библиотека для работы с дисплеем
#include <QuadDisplay2.h>
// создаём объект класса QuadDisplay и передаём номер пина CS
QuadDisplay qd(10);
// библиотека для работы с GPS устройством
#include <TroykaGPS.h>
// создаём объект класса GPS и передаём в него объект Serial1 
GPS gps(Serial1);
// задаём размер массива для времени, даты, широты и долготы
#define MAX_SIZE_MASS 16
// макросы
#define SPEED         0
#define DISTANCE      1
// переменые для хранения текущей скорости и пройденного расстояния
float speed;
float distance = 0;
// переменные для хранения координат
float latitudeLast;
float longitudeLast;
// режим отображение: текущей скорости / пройденного расстояния 
bool state = SPEED;
// флаг первого считывания данных с GPS-модуля
bool firstStart = true;
 
void setup()
{ 
  // открываем последовательный порт для мониторинга действий в программе
  Serial.begin(115200);
//  Serial.print("Serial init OK\r\n");
  // открываем Serial-соединение с GPS-модулем
  Serial1.begin(115200);
  // инициализация дисплея
  qd.begin();
  // инициализация кнопки
  button.begin();
}
 
void loop()
{
  // переменные для хранения текущих координат
  float latitude;
  float longitude;
  // считывание данных с кнопки
  button.read();
  // если был клик
  if (button.justPressed()) {
    // меняем отображение скорости на расстояния или наоборот
    state = !state;
    // в зависимости от текущего состояния спидометра
    if (!state) {
      qd.displayDigits(QD_S, QD_P, QD_E, QD_d);
    } else {
      qd.displayDigits(QD_d, QD_I, QD_S, QD_t);
    }
    delay(1000);
  }
  // считываем состояние GPS-модуля
  switch(gps.getData()) {
    // всё OK
    case GPS_OK:
      // считываем с GPS-модуля текушие координаты и скорость
      speed = gps.getSpeedKm();
      latitude = gps.getLatitudeBase10();
      longitude = gps.getLongitudeBase10();
      // если данные с GPS-модуля пришли впервые
      if (firstStart) {
        // сохраняем текущие значения координат
        latitudeLast = latitude;
        longitudeLast = longitude;
        // первый запуск был совершен
        firstStart = false;
      }
      distance = distance + latlng2distance(latitudeLast, longitudeLast, latitude, longitude);
      // сохраняем текущие значения координат
      latitudeLast = latitude;
      longitudeLast = longitude;
/*      Serial.print(speed);
      Serial.print("\t");
      Serial.print(latitude, 6);
      Serial.print("\t");
      Serial.print(longitude, 6);
      Serial.print("\t"); 
      Serial.println(distance);*/
      // в зависимости от режима работы спидометра
      if(!state) {
        // выводим текущую скорость
        qd.displayFloat(speed, 1);
      } else {
        // выводим текущее расстояние
        qd.displayFloat(distance, 1);
      }
      break;
    // ошибка данных
    case GPS_ERROR_DATA:
      Serial.println("GPS error data");
      break;
    // нет соединение со спутниками
    case GPS_ERROR_SAT:
      Serial.println("GPS no connect to satellites");
      qd.displayDigits(QD_E, QD_r, QD_r, QD_NONE);
      break;
  }
}
 
float latlng2distance(float lat1, float long1, float lat2, float long2) {
    //радиус Земли
    unsigned long R = 6372795;
    //перевод коордитат в радианы
    lat1 *= 3.14 / 180;
    lat2 *= 3.14 / 180;
    long1 *= 3.14 / 180;
    long2 *= 3.14 / 180;
    //вычисление косинусов и синусов широт и разницы долгот
    float cl1 = cos(lat1);
    float cl2 = cos(lat2);
    float sl1 = sin(lat1);
    float sl2 = sin(lat2);
    float delta = long2 - long1;
    float cdelta = cos(delta);
    float sdelta = sin(delta);
    //вычисления длины большого круга
    float y = sqrt(pow(cl2 * sdelta, 2) + pow(cl1 * sl2 - sl1 * cl2 * cdelta, 2));
    float x = sl1 * sl2 + cl1 * cl2 * cdelta;
    float ad = atan2(y, x);
    float dist = ad * R; //расстояние между двумя координатами в метрах
    return dist;
}

Что дальше?

Хотите собрать другой девайс? Выберите своё будущее устройство из списка проектов на Slot Shield.

Часто задаваемые вопросы

Где скачать необходимые библиотеки и как их установить?
У моего модуля QuadDisplay всего три ноги и расположены они слева. Можно ли использовать его в этом проекте?

Да, модуль можно использовать, но скетч, библиотеки и схема сборки отличаются.

Мы уже тестируем версию скетча для старого дисплея и разместим её на этой странице в ближайшие дни.