Proiect Ceas GPS cu ecran TFT

Sistemul de poziționare GPS este un real ajutor pentru navigație și localizare dar este și un foarte bun furnizor de oră exactă. În cadrul acestui proiect vom construi un ceas care își preia informațiile de oră și dată prin intermediul unui modul GPS. Interfațarea cu utilizatorul va fi asigurată de un ecran TFT de 2.8’’ cu touch capacitiv. Captura de ecran de mai jos ilustrează scopul final al sistemului (afișare oră și dată, bulinele albastre din partea de sus a ecranului indică numărul de sateliți GPS văzuți direct de sistem). Sincronizarea ceasului prin GPS oferă o foarte mare acuratețe și o independență a sincronizării față de un tip de rețea anume (rețea de telefonie mobilă, rețea Internet) putând funcționa oriunde în lume, chiar și în zone extrem de izolate.

1

Ecran principal ceas GPS

Pentru mai multe informații legate de funcționarea sistemului de localizare și sincronizare GPS puteți consulta:

GPS.gov: GPS Overview

http://www.gps.gov/systems/gps/

Global Positioning System

https://en.wikipedia.org/wiki/Global_Positioning_System

Garmin | What is GPS?

http://www8.garmin.com/aboutGPS/

The Global Positioning System

http://www.colorado.edu/geography/gcraft/notes/gps/gps_f.html

Pentru implementarea sistemului vom utiliza:

1. placă de dezvoltare Arduino Uno R3 (sau echivalentă):

2

https://www.robofun.ro/arduino/arduino_uno_v3

2. un Sparkfun GPS Logger Shield (dar programul va funcționa cu orice modul GPS serial); pentru utilizare această componentă necesită și un Kit Conectori Arduino R3 și o baterie 12mm (CR1225):

3

https://www.robofun.ro/gps/gps-logger-shield-sparkfun

Pentru mai multe informații despre funcționarea acestei componente puteți consulta:

GPS Logger Shield Hookup Guide

https://learn.sparkfun.com/tutorials/gps-logger-shield-hookup-guide

3. un ecran Adafruit 2.8″ TFT Touch Shield (240x320pixels, 262K culori)

4

https://www.robofun.ro/lcd/tft-touch-shield-arduino-capacitive

Pentru mai multe informații despre funcționarea acestei componente puteți consulta:

Adafruit 2.8″ TFT Touch Shield v2 – Capacitive or Resistive

https://learn.adafruit.com/adafruit-2-8-tft-touch-shield-v2

Ordinea de instalare a celor două componente de tip shield este: shield-ul GPS se conectează la placa de dezvoltare Arduino Uno iar shield-ul TFT se conectează la shield-ul GPS (ordinea inversă de montare a celor două shield-uri ar face imposibilă utilizarea ecranului…).

Pentru programarea sistemului sunt necesare mai multe biblioteci software externe mediului Arduino IDE:

TinyGPS++ – pentru interpretarea datelor provenite de la modulul GPS

http://arduiniana.org/libraries/tinygpsplus/

Adafruit GFX și Adafruit ILI9341 – pentru partea de afișare pe ecranul TFT

https://github.com/adafruit/Adafruit-GFX-Library

https://github.com/adafruit/Adafruit_ILI9341

Pentru înțelegerea modului de lucru cu ecrane grafice utilizând Adafruit GFX este utilă parcurgerea:

https://learn.adafruit.com/adafruit-gfx-graphics-library

Adafruit FT6206 – necesară pentru lucrul cu partea de touchscreen

https://github.com/adafruit/Adafruit_FT6206_Library

#include <SoftwareSerial.h>

#include <TinyGPS++.h>

TinyGPSPlus tinyGPS;

#define GPS_BAUD 9600

#define ARDUINO_GPS_RX 9

#define ARDUINO_GPS_TX 8

SoftwareSerial gpsPort(ARDUINO_GPS_TX, ARDUINO_GPS_RX);

#include <SPI.h>

#include <Adafruit_GFX.h>

#include <Adafruit_ILI9341.h>

#include <Adafruit_FT6206.h>

#define TFT_DC 9

#define TFT_CS 10

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

Adafruit_FT6206 ctp = Adafruit_FT6206();

Din partea de declarații inițiale se poate observa o mică incompatibilitate între cele două componente de tip shield și anume: pinul D9 al plăcii de dezvoltare este utilizat atât pentru semnalul RX al modulului GPS cât și pentru semnalul DC al ecranului TFT. Din fericire această incompatibilitate nu va afecta programul nostru deoarece nu vom trimite comenzi către modulul GPS, doar vom prelua date de la modulul GPS, și nici nu vom trece modulul TFT în modul Command (pinul DC configurează modul de funcționare Data/Command). Cu alte cuvinte pinul este folosit de ambele componente dar nu este utilizat pentru nici una dintre ele în cadrul programului prezentat.

În cadrul secțiunii setup() se vor inițializa componentele declarate anterior (tft – pentru afișaj, ctp – pentru touch și gpsPort – pentru comunicația cu modulul GPS):

void setup() {

  tft.begin();

  ctp.begin(40);

  gpsPort.begin(GPS_BAUD);

  delay(100);

  tft.fillScreen(ILI9341_DARKGREEN);

  yield();

  delay(1000);

}

Se vor utiliza următoarele variabile globale pentru a controla procesul de afișare:

  • timer – numărul de milisecunde de la ultima reîmprospătare a informațiilor de pe ecran; reîmprospătarea se va face o dată la 60.000ms = 60s = 1 minut;
  • show – tipul de informații ce vor fi afișate pe ecran (0 – ceas, 1 – informații GPS, 2 – distanțe față de câteva orașe din România);
  • timezone – ora returnată de sistemul GPS este în fusul orar UTC+0 (ora Londrei); în această variabilă vom calcula decalajul de fus orar bazându-ne pe longitutinea raportată de GPS;
  • Months – vector cu denumirea lunilor anului;
  • ref – variabilă logică care forțează (când este true) reafișare imediată a informațiilor de pe ecran.

uint32_t timer = millis();

byte show = 0;

byte timezone = 0;

const char * Months[12] = { “Ianuarie”, “Februarie”,

 “Martie”, “Aprilie”, “Mai”, “Iunie”, “Iulie”, “August”,

“Septembrie”, “Octombrie”, “Noiembrie”, “Decembrie” };

boolean ref = true;

În cadrul secțiunii loop() se vor citi informațiile venite de la modulul GPS:

void loop(void) {

  while (gpsPort.available())

    tinyGPS.encode(gpsPort.read());

În cazul în care se atinge ecranul se va comuta între cele trei moduri de afișare (ceas, GPS, distanțe – ultimele două sunt exemplificate în următoarele două capturi de ecran):

5

6

if (ctp.touched()) {

    show++;

    show = show % 3;

    ref = true;

  }

La fiecare 60 de secunde afișajul este reîmprospătat cu tipul de informații ales de utilizator (0, 1 sau 2):

 

if ((millis() – timer > 60000) || ref) {

      tft.fillScreen(ILI9341_BLACK);

      yield();

      switch (show) {

În modul 0 (ceas) se afișează ora, data și numărul de sateliți. Tot aici se calculează diferența de fus orar în mod automat plecând de la premiza că la fiecare 15 grade se adaugă o oră, mai multe informații se pot consulta și la adresa:

Latitude, Longitude and Time Zones – the Earth’s Coordinate System

http://www.english-online.at/geography/longitude-latitude/longitude-latitude-time-zones.htm

case 0:

      if (tinyGPS.time.isValid()) {

         tft.setTextSize(8);     

         tft.setTextColor(ILI9341_RED); 

         tft.setRotation(1);

         tft.setCursor(50, 90);

         if (((tinyGPS.time.hour() + timezone) % 24) < 10) tft.print(‘0’);

         if (tinyGPS.location.isValid()) timezone = ((tinyGPS.location.lng()-7.5) / 15)+1;

         tft.print((tinyGPS.time.hour() + timezone) % 24);

        tft.print(‘:’);

         if (tinyGPS.time.minute() < 10) tft.print(‘0’);

                tft.print(tinyGPS.time.minute()); }

      if (tinyGPS.date.isValid()) {

         tft.setTextColor(ILI9341_ORANGE); 

         tft.setTextSize(3);

         tft.setCursor(10, 200);

         tft.print(tinyGPS.date.day()); tft.print(” “);

         tft.print(Months[tinyGPS.date.month()]);

         tft.print(” “);

         tft.print(tinyGPS.date.year()); }

      for (int i=0; i<tinyGPS.satellites.value(); i++)

         tft.fillCircle(300-(25*i),20,10,ILI9341_CYAN);

      break;

În modul 1 – informații GPS – se vor afișa: altitudinea în metri, latitudine, longitudine, acuratețea relativă a poziționării pe orizontală HDOP (horizontal dilution of precision), timpul scurs de la ultima sincronizare AGE, viteza și cursul (ultimii doi parametrii au relevanță doar dacă sistemul este în mișcare).

case 1: 

      if (tinyGPS.location.isValid()) {

        tft.setTextSize(3);

        tft.setRotation(1);

        tft.setCursor(0, 25);

        tft.setTextColor(ILI9341_GREEN);

        tft.print(“ALT: “);

    tft.print(tinyGPS.altitude.meters());

    tft.println(“m”);

        tft.setTextColor(ILI9341_GREEN+5);

        tft.print(“LAT: “);

        tft.println(tinyGPS.location.lat(), 5);

        tft.setTextColor(ILI9341_GREEN+10);

        tft.print(“LON: “);

        tft.println(tinyGPS.location.lng(), 5);

        tft.setTextColor(ILI9341_GREEN+15);

        tft.print(“HDOP: “);

        tft.println(tinyGPS.hdop.value(), 5);

        tft.setTextColor(ILI9341_GREEN+20);

        tft.print(“AGE: “);

        tft.print(tinyGPS.location.age(), 5);

        tft.println(“ms”);

        tft.setTextColor(ILI9341_GREEN+25);

        tft.print(“SPD: “);

tft.print(tinyGPS.speed.kmph(), 5);

tft.println(“kmph”);

        tft.setTextColor(ILI9341_GREEN+30);

        tft.print(“CRS: “);

tft.println(tinyGPS.course.deg(), 5);  }

  break;

În modul 2 – distanțe – se vor afișa distanțele (în kilometri) dintre punctul în care se află sistemul și câteva orașe din România (București, Iași, Constanța, Cluj, Piatra Neamț, Oradea și Satu Mare). Atenție!!! distanța este calculată în linie dreapta pe baza coordonatelor GPS, nu este distanță rutieră sau feroviară.

case 2:

      if (tinyGPS.location.isValid()) {

        tft.setTextSize(3);

        tft.setRotation(1);

        tft.setCursor(0, 25);

        tft.setTextColor(ILI9341_PINK);

        #define BUC_LAT 44.439663

        #define BUC_LON 26.096306

        #define IAS_LAT 47.151726

        #define IAS_LON 27.587914

        #define COS_LAT 44.1833

        #define COS_LON 28.65

        #define CLJ_LAT 46.7772

        #define CLJ_LON 23.5999

        #define PTN_LAT 46.929962

        #define PTN_LON 26.377979

        #define ORD_LAT 47.0666667

        #define ORD_LON 21.9333333

        #define STM_LAT 47.790001

        #define STM_LON 22.889999

        tft.setTextColor(ILI9341_YELLOW);

        unsigned long distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),

BUC_LAT,BUC_LON)/1000;

    tft.print(“Bucuresti: “);

tft.print(distanceKm);

tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+5);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),

IAS_LAT,IAS_LON)/1000;

    tft.print(“Iasi:      “);

tft.print(distanceKm);

tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+10);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),COS_LAT,COS_LON)/1000;

    tft.print(“Constanta: “);

    tft.print(distanceKm);

    tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+15);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),CLJ_LAT,CLJ_LON)/1000;

tft.print(“Cluj:      “);

tft.print(distanceKm);

tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+20);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),PTN_LAT,PTN_LON)/1000;

tft.print(“Piatra Nt: “);

tft.print(distanceKm);

tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+25);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),

ORD_LAT,ORD_LON)/1000;

    tft.print(“Oradea:    “);

tft.print(distanceKm);

tft.println(“Km”);

    tft.setTextColor(ILI9341_YELLOW+30);

    distanceKm = (unsigned long)

TinyGPSPlus::distanceBetween

(tinyGPS.location.lat(),

tinyGPS.location.lng(),STM_LAT,STM_LON)/1000;

    tft.print(“Satu Mare: “);

tft.print(distanceKm);

tft.println(“Km”); }

  break;    }

timer = millis();

ref = false;

  }

  yield();

}

Programul a fost dezvoltat și testat cu Arduino IDE 1.6.12, Arduino AVR Boards 1.6.14 și bibliotecile TinyGPSPlus 0.94b, Adafruit GFX 1.1.5, Adafruit ILI9341 1.0.2, Adafruit FT6206 1.0.0 .

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s