GPSからは位置の信号はもちろんですが非常に正確なクロック信号が送られています。
今回はこの信号を受信して正確な時間を表示する時計を作ってみました。同時に
温度、湿度、気圧を測定するモジュールBME280を使い環境データも表示しました。

  • 使用デバイス(ArduinoMega,GPSモジュールNEO-6M,環境測定BME280,2.8インチTFT Arduino用LCDシールド)

    GPSクロックの写真です

    XXの写真

    プログラムコードです
    GPSモジュールからは標準時間(UTC)データが送られてきますので日本時間(JST)に変換する必要があります。
    9時間進めるのはもちろんですが月(Month)の変化点、年(Year)の変化点、閏年(Leap Year)の考慮など結構面倒な処理が必要でした。
    ArdunoMegaはメモリーは多いですが処理能力は高くなくこのプログラムを動作させるのは限界です。
    したがってさらに安価で処理能力の高いESP32を使うことをお勧めします。

      
     

    /* TFTにカレンダーを表示するプログラム 2020/11/1 Revised 11月判定修正 2021/05 3月判定missing修正 使用TFT:LCD9431 */ #include "SPI.h" #include "Adafruit_GFX.h" // Core graphics library #include "Adafruit_TFTLCD.h" // Hardware-specific library // The control pins for the LCD can be assigned to any digital or // analog pins...but we'll use the analog pins as this allows us to // double up the pins with the touch screen (see the TFT paint example). #define LCD_CS A3 // Chip Select goes to Analog 3 #define LCD_CD A2 // Command/Data goes to Analog 2 #define LCD_WR A1 // LCD Write goes to Analog 1 #define LCD_RD A0 // LCD Read goes to Analog 0 #define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin // Assign human-readable names to some common 16-bit color values: #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); // create an instance of the library String priorday; String priorhour; int day_change=1; char sensorPrintout[4]; int initialFlag=0; //BME280 #include "Wire.h" #include "Adafruit_Sensor.h" #include "Adafruit_BME280.h" #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME280 bme; // I2C unsigned long delayTime; void setup() { Serial.begin(9600); Serial1.begin(9600); //For GPS READ #ifdef USE_ADAFRUIT_SHIELD_PINOUT Serial.println(F("Using Adafruit 2.4\" TFT Arduino Shield Pinout")); #else Serial.println(F("Using Adafruit 2.4\" TFT Breakout Board Pinout")); #endif Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height()); tft.reset(); uint16_t identifier = tft.readID(); if(identifier == 0x9325) { Serial.println(F("Found ILI9325 LCD driver")); } else if(identifier == 0x9328) { Serial.println(F("Found ILI9328 LCD driver")); } else if(identifier == 0x4535) { Serial.println(F("Found LGDP4535 LCD driver")); }else if(identifier == 0x7575) { Serial.println(F("Found HX8347G LCD driver")); } else if(identifier == 0x9341) { Serial.println(F("Found ILI9341 LCD driver")); } else if(identifier == 0x8357) { Serial.println(F("Found HX8357D LCD driver")); } else if(identifier==0x0101) { identifier=0x9341; Serial.println(F("Found 0x9341 LCD driver")); }else { // Serial.print(F("Unknown LCD driver chip: ")); // Serial.println(identifier, HEX); // Serial.println(F("If using the Adafruit 2.8\" TFT Arduino shield, the line:")); // Serial.println(F(" #define USE_ADAFRUIT_SHIELD_PINOUT")); // Serial.println(F("should appear in the library header (Adafruit_TFT.h).")); // Serial.println(F("If using the breakout board, it should NOT be #defined!")); // Serial.println(F("Also if using the breakout, double-check that all wiring")); // Serial.println(F("matches the tutorial.")); identifier=0x9341; } tft.begin(identifier); tft.fillScreen(BLACK); // GPS setup //NMEA Checksum https://nmeachecksum.eqth.net/ // set the data rate for the SoftwareSerial port ////NMEA Checksum https://nmeachecksum.eqth.net/ Serial1.begin(9600); Serial1.print("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n"); //出力限定 No output /// 出力する場合は mySerial.print("$PUBX,40,GLL,0,1,0,0,0,0*5D\r\n"); // Serial1.print("$PUBX,40,GLL,0,1,0,0,0,0*5D\r\n"); Serial1.print("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n"); //出力限定 No output Serial1.print("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n"); //出力限定 No output Serial1.print("$PUBX,40,GGA,0,0,0,0,0,0*5A\r\n"); //出力限定 No output /// 出力する場合は mySerial.print("$PUBX,40,GGA,0,1,0,0,0,0*5B\r\n"); Serial1.print("$PUBX,40,GSA,0,0,0,0,0,0*4E\r\n"); //出力限定 No output //delay(300); //BME280_Setup Serial.println(F("BME280 test")); bool status; // default settings // (you can also pass in a Wire library object like &Wire2) status = bme.begin(0x76); if (!status) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } Serial.println("-- Default Test --"); delayTime = 1000; Serial.println(); tft.setRotation(1); tft.setTextColor(YELLOW,BLACK); tft.setCursor(30, 120); tft.setTextSize(3); tft.print("T="); tft.setCursor(30, 150); tft.setTextSize(3); tft.print("P="); tft.setCursor(30, 180); tft.setTextSize(3); tft.print("H="); } void loop() { //GPS Read Test------- // String dtstr; String dummy,dummy2; int k; long hhmmss; long ddmmyy; String YY,MT,DD,HH,MM,SS; String dumY,dumT; int pos1,pos2; int uyear,umonth,uday,uhh,umin,usec; String line = Serial1.readStringUntil('\n'); /////Serial.println(line); // Serial.println(line.length()); //delay(400); if(line != ""){ int i, index = 0, len = line.length(); String str = ""; if(MidDol(line,1,6)=="$GPRMC" ) { /////Serial.println(line); k=findkosuu(line,","); //---Time Block extract------ pos1=findposition(line,",",1)+2; pos2=findposition(line,",",2); dumT=MidDol(line,pos1,pos2); ////U Time extract //---Year Block extract------ pos1=findposition(line,",",9)+2; pos2=findposition(line,",",10); dumY=MidDol(line,pos1,pos2); hhmmss = dumT.toInt(); //1sec plus uhh = hhmmss / 10000; umin = (hhmmss / 100) % 100; usec = hhmmss % 100 % 60; //Avoid 60Sec ddmmyy = dumY.toInt(); uyear = 2000+ddmmyy % 100; umonth = (ddmmyy / 100) % 100; uday = ddmmyy / 10000; dummy=UTC2JST (uyear,umonth,uday,uhh,umin,usec); /// dummy="2019/3/27/11:15:17"; //speed test //Serial.println(dummy); // dummy.concat(" "); //0秒~9秒までのエラー回避 // Serial.println(UTC2JST (u_year,u_month,u_day,u_hh,u_min,u_sec) ); tft.setCursor(0, 60); if(line.length()>60){tft.setTextColor(CYAN,BLACK);} else{tft.setTextColor(RED,BLACK);} tft.setCursor(40, 60); tft.setTextSize(5); tft.println(MidDol(dummy,12,19)); ///年月の表示 tft.setCursor(10, 5); if(line.length()>60){tft.setTextColor(CYAN,BLACK);} else{tft.setTextColor(RED,BLACK);} tft.setTextSize(5); if(initialFlag==0) { tft.println(MidDol(dummy,1,10));initialFlag=1; } //最初は日付表示 if(initialFlag==1 && day_change==1){ tft.println(MidDol(dummy,1,10)); } //日付が変わらない間は表示しない } //TempPressHumid表示 if (usec==0){ tft.setTextColor(YELLOW,BLACK); //tft.setRotation(1); tft.setCursor(80, 120); tft.setTextSize(3); // tft.print("Temperature = "); tft.print(bme.readTemperature()); tft.println(" *C"); tft.setCursor(80, 150); // tft.print("Press = "); tft.print(bme.readPressure() / 100.0F); tft.println(" hPa"); tft.setCursor(80, 180); // tft.print("Humidity = "); tft.print(bme.readHumidity()); tft.println(" %"); } } } //-------GPS Sub Routine----------------------------------------------- String MidDol(String str,int From,int To){ String dummy; int lgth; lgth=str.length(); if (To > lgth){To=lgth;}; dummy=str.substring(From-1, To); return dummy;} // ------------------------------------------------------ String MidDoln(String str,int From,int n){ String dummy; int lgth; lgth=str.length(); if (From-1+n> lgth){dummy="ErrorAreaExceed!";return dummy;}; dummy=str.substring(From-1, From-1+n); return dummy; } //-------------------------------------------------------- //データの中のある文字の個数を見つける int findkosuu(String str,String moji) { int i; int n; int len; len=str.length(); n=0; for (int i=0; i <= len; i++){ // Serial.println(str.substring(i, i+1)); if(str.substring(i, i+1)== moji) {n++;}; } return n; } //--------------------------------------------------------- //データの中のある文字のn番目の位置を見つける int findposition(String str,String moji,int n){ int i; int pos; int m=0; int len; len=str.length(); for (int i=0; i <= len; i++){ if (str.substring(i, i+1)==moji) {m++;}; if (m==n) {return i;} }; } //--------------------------------------------------------- //---------UTCtoJST_Conversion--------------- String UTC2JST (int u_year,int u_month,int u_day,int u_hh,int u_min,int u_sec) { String str; int j_year; int j_month; int j_day; int len; int dyear; int dmonth; int dday; int dhh; // int day_change; int month_change; int year_change; int k; day_change=0; month_change=0; year_change=0; dyear=u_year; dmonth=u_month; dday=u_day; dhh=u_hh ; //hour hantei if (u_hh >=15) { dhh= (dhh+9)% 24; dday=dday+1;day_change=1; }; if (u_hh <15) { dhh= (dhh+9); day_change=0 ;}; if ((day_change==1 && dday==31) && (u_month==4 || u_month==6 || u_month==9 || u_month==11)) {dday=1;dmonth=u_month+1;month_change=1;}; if ((day_change==1 && dday==32) && (u_month==3 ||u_month==5 || u_month==7 || u_month==8 || u_month==10 || u_month==12)) {dday=1;dmonth=(u_month)%12+1;month_change=1;}; if ( month_change==1 && dmonth==1) { dyear=u_year+1;}; if ((day_change==1 && dday==29) && dyear%4!=0 && u_month==2 ) //not leap year {dday=1;dmonth=u_month+1;month_change=1;}; if ((day_change==1 && dday==30) && dyear%4==0 && u_month==2 ) //leap year {dday=1;dmonth=u_month+1;month_change=1;}; j_year=dyear; j_month=dmonth;j_day=dday; str=String(j_year); //Serial.println(str); str.concat(String("/")); if (j_month<10){str.concat("0");}; str.concat(String(j_month)); //Serial.println(str); str.concat(String("/")); if (j_day<10){str.concat("0");}; str.concat(String(j_day)); //Serial.println(str); str.concat(String("/")); if (dhh<10){str.concat("0");}; str.concat(String(dhh)); //Serial.println(str); str.concat(String(":")); if (u_min<10){str.concat("0");}; str.concat(String(u_min)); //Serial.println(str); str.concat(String(":")); if (u_sec<10){str.concat("0");}; str.concat(String(u_sec)); //Serial.println(str); return str; } //-------------GPS Sub Routine END----------------------------------------------