Я начал думать об этом проекте еще летом, когда оставил листок бумаги около лобового стекла в машине. Я был удивлён тем, что отражение листа в стекле было хорошо читаемым, и я решил попробовать отобразить что-нибудь более полезное с помощью проектора на лобовое стекло.






После недолгих поисков, я обнаружил, что модуль GPS и матрица LED для Ардуино достаточно дешевы, и приступил к сборке автомобильного HUD проектора на лобовое стекло.

Необходимые компоненты (в скобках указаны фразы, по которым можно найти в интернете нужные компоненты):

  • Ардуино Про Мини 5V/16MHz
  • Модуль GPS (Ublox Neo-M8N module chip UART TTL Smart GPS gnss antenna dual GLONASS receiver)
  • LED дисплей (MAX7219 Dot Matrix Module For Arduino Microcontroller 4 In One Display)
  • Фоторезистор (5549 Light Dependent Resistor LDR 5MM)
  • Понижающий конвертер вольтажа (Mini360 DC-DC Buck Converter Step Down Module)
  • Резисторы
  • Провода
  • Опционально: светодиоды

Можно использовать Ардуино любого типа — я работал с дешевым китайским клоном, использующим логику 5V TTL. Если вы будете использовать тот же тип, что и я, то для загрузки кода на Ардуино вам понадобится конвертер с USB на TTL. Конвертер нужен лишь для загрузки кода и в рабочем режиме он не используется.

На Ибэй и Алиэкспресс можно найти множество разных модулей GPS. Большинство из них работают от 3.3V, но я решил немного доплатить и купить версию с логикой 5V TTL, поэтому мне не понадобились дополнительные схемы трансляции сигналов Rx/Tx для разных уровней вольтажа. Я просто купил конвертер вольтажа с 12V на 5V. Модуль мог принимать сигналы со спутников GPS, GLONASS и BAIDOU, а встроенная антенна была приятным бонусом.

Дисплей

Для моих потребностей он оказался слишком большим, ведь для отображения скорости авто нужно вывести на экран всего три цифры. Так что дисплея, вполовину меньшего, вполне бы хватило. Хорошо, что макетную плату можно разделить, просто аккуратно распилив её.

Яркость дисплея регулируется в зависимости от яркости окружающей обстановки. Более ярко в солнечный полдень, менее ярко в туннеле. Делитель напряжения создается из LDR 5549 и резистора на 150K — вольтаж зависит от света, замеренного через аналоговый порт A0 на Ардуино.

Можно опционально использовать два или один светодиод для индикации GPS. В случае с двумя диодами, я использовал зеленый и красный диоды, соединённые с пинами D8 и D9. Зеленый означал фиксацию 2D, красный — 3D. После примерно полугода использования, я осознал, что они не нужны мне, но если вам интересно, то можно также использовать пин PPS от GPS модуля. Если GPS захвачен, то он начинает подавать на пин импульсы 5V.

Несколько слов о питании

Все компоненты рассчитаны на 5V, поэтому нам нужно понизить напряжение с 12V (или 24V) до 5V. У меня уже был хороший опыт использования конвертера DC-DC, о котором я упоминал выше. Он небольшой по размеру, дешевый и может держать ток до 2A (что значительно выше, чем нам нужно, ведь девайсу требуется всего около 150mA). Выходное напряжение регулируется небольшой отвёрткой — удостоверьтесь, что выставили 5V перед тем, как подключите Ардуино и остальные модули.

Входное напряжение можно получить от прикуривателя автомобиля, либо от питания, подающегося на радио. Но будьте осторожны, если вы не знаете, откуда взять 12V — используйте прикуриватель.

Код для Ардуино

// Speed HeadUp display with GPS

#include  // Is possible to use common SoftwareSerial library
#include    //Genial library for matrix displays https://github.com/MajicDesigns/MD_Parola
#include 
#include 

//Settings for display
// Define the number of devices we have in the chain and the hardware interface of display
#define MAX_DEVICES 2
#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10
// Hardware SPI connection
MD_Parola P = MD_Parola(CS_PIN, MAX_DEVICES);

//OPTIONAL - Settings for info LED
int GreenLED = 9;
int RedLED = 8;
//end of OPTIONAL

char buffer[99];
String veta;
byte ptr;
char ch;

int pozice;
String fix;
int fix_cislo;
float rychlost;
int rychlost_int;

int intensity = 0;
int svetlo;

NeoSWSerial gps(4, 3); // RX, TX - correct to match

void setup()
{
 Serial.begin(9600);
 gps.begin(9600); // adjust speed to communicate with GPS module
 
Serial.println("Start");

pinMode(GreenLED, OUTPUT); 
pinMode(RedLED, OUTPUT); 
digitalWrite(RedLED, LOW);
digitalWrite(GreenLED, LOW);

  P.begin();//start of display
  P.setZoneEffect(0, true, PA_FLIP_LR);  //PA_FLIP_LR means - mirrored
  P.setIntensity(0);                     //initial intensity of brightness  0-15 steps 
  P.print("---");
  delay(2000);

}

void loop()
{
//measuring of ambient light
 svetlo  = analogRead(0); 
 if (svetlo  0){     //searching for fix
         fix = veta.substring(8,9);
         //Serial.print("fix= ");
         //Serial.print(fix);
         //Serial.println("  ");
         //Serial.println(veta);

         char buf[fix.length()+1];                //conversion String to array of char - we need use atoi()function and it works only with array of char
         fix.toCharArray(buf, fix.length()+1);
         fix_cislo = atoi(buf);                   //final int number
                }     //End of fix search
                
         
        if (fix_cislo > 1){       //searching for speed
                  pozice = veta.indexOf("RMC"); // in xxRMC is stored information about time, pozition, speed etc 
                       if (pozice > 0){
                        
                        pozice = veta.indexOf("E");   //position of speed is in NMEA is fixed to 3th character from E or W in coordinates  
                       // Serial.print("pozice E= ");   //so we look for E - if not present in NMEA sentence, position is set to -1 and we have to look for W
                       // Serial.println(pozice);
                        if (pozice < 0){pozice = veta.indexOf("W");
                       // Serial.print("pozice W= ");
                       // Serial.println(pozice);}
                        
                   fix = veta.substring(pozice+2,pozice+7);
                   rychlost = fix.toFloat();
                   rychlost = rychlost*1.852;         //Original speed from NMEA is in KNOTS... So we have to convert it to km/h 
                                                          // 1 knots =1.85200 kilometers per hour
                   //rychlost = rychlost*1.15077945;      // 1 knots = 1.15077945 miles per hour
                   rychlost_int=(int)rychlost; //conversion from float to int - int is enough to display :-)
                   if (rychlost_int < 2){rychlost_int=0;} //in small speed GPS give not precise results 
                   P.print(rychlost_int);  //Print speed to display
                   
                   }    //End of speed search
        }
        else{
         P.print("***");  //if GPS is not fixed print *** to display 
          }        
      
        ptr=0;
        break;
      }
      default:
      {
        buffer[ptr++]=ch;
        break;
      }
   }  

     
     //OPTIONAL -- LED indicates of fix mode - If not used, remowe this section
     if (fix_cislo==2){digitalWrite(GreenLED, HIGH);digitalWrite(RedLED, LOW);}     //2D fix  
     else if (fix_cislo==3){digitalWrite(GreenLED, LOW);digitalWrite(RedLED, HIGH);} //3D fix
     else {digitalWrite(GreenLED, LOW);digitalWrite(RedLED, LOW);}
     //OPTIONAL - Led - end of section
     
 }
 P.setIntensity(intensity);  //Setting of display brightness
}

О коде

Библиотека для коммуникации должна быть обычной библиотекой SoftwareSerial, я использовал библиотеку NeoSWSerial, так как она немного меньше по размеру. Вы можете просто использовать пины Rx/TX на Ардуино и избежать использования SoftwareSerial, но в таком случае, вам нужно будет переписать часть кода.

Для работы с дисплеем используется библиотека PAROLA. Она позволяет нам отображать отзеркаленный текст, используя всего одну команду.

Модуль GPS в текстовом режиме выдает напрямую информацию о времени, положении, количестве спутников, скорости и тд. Всё устанавливается протоколом NMEA. Я пробовал использовать кое-какие библиотеки для парсинга GPS, но, в конце концов, решил парсить данные NMEA-сообщений напрямую, без использования библиотек. Всё свелось к тому, что доступные библиотеки слишком большие и предоставляют много лишней информации.

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

rychlost = rychlost*1.852; //Оригинальная скорость из NMEA в УЗЛАХ… Поэтому мы конвертируем её в км/час

//rychlost = rychlost*1.15077945; //Эта строка кода для перевода узлов в мили

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

Файлы