Arduinoの機能を拡張するシールドは様々なものがでており、LANや無線LAN、モーター制御やLCD制御等複雑な電子機器の制御がArduinoの基板上に乗せるだけで簡単に出来るようになっている。ソフト的にもライブラリを追加してクラスや関数を少し呼び出すだけで使えてしまう便利な世の中になっている。
GPSシールドもその一つで、Arduinoで簡単にGPS情報を取得し扱う事が出来る。今回使用しているのはElecrow製のGPSシールドで、NEO-6Mと言うモジュールが使われている。
http://www.elecrow.com/wiki/index.php?title=GPS_shield
SDカードスロットもついているが、今回SDカードの使い方については割愛する。SDカードはライブラリを使用して読み書き出来るので、GPSのログ等の記録に使えそうだ。
http://www.elecrow.com/wiki/index.php?title=Wireless_SDshield
GPSの使い方は簡単で、ライブラリすら必要無い。GPS情報がRX/TXピンからシリアル通信でどんどん流れてくるだけだ。
シールド上のジャンパピンでどのピンをRX/TXにするか設定し、後はSoftwareSerialでピンを指定し、9600bpsで開けばどんどんデータが流れてくる。ピンを0/1に指定すれば、シリアルモニタを開いた途端GPSの情報が表示される。
とりあえずGPSの情報を見て楽しみたいのであれば、無償で提供されているu-centerのソフトをインストールし、Arduinoのポートを指定して開けば各種情報が表示される。
http://www.elecrow.com/wiki/images/4/4a/U-centersetup-7.0.2.1.zip
BluetoothモジュールをArduinoに追加すれば、GPS情報を離れたPC等から見ることも出来る。
さて、GPSロガー等色々用途は考えられるが、使い方の例としてLCDシールドと組み合わせてGPS時計を作ってみよう。
しかし、単純なGPS時計だけでは他でも紹介されていて芸が無いので、さらに組み合わせてGPS時計で時計合わせをした後、JJY信号を発信し電波時計の調整が出来る様にしてみる。
GPSシールドの性能はなかなか良く、電波時計の電波が入らない屋内でもGPS情報は取得出来たりしたので(位置までは計測出来ないが)、GPS信号は入るが電波時計が入らない場所に設定してある電波時計を調整したり、海外で日本で購入したお気に入りの電波時計を調整するのにも使えるかもしれない。
SainsmartのLCDシールドとの組み合わせで、GPSシールドはRX/TXを0/1ピンに設定(このため通信にソフトシリアルでなく通常シリアル使用)、JJY信号の発信にはピン3を使用している。配線はArduinoの上にGPSシールド、その上にLCDシールドをスタックするだけ。Digital Pin3とGNDの間にJJYのアンテナケーブル。
LCDシールドのピンアサインは下記参照。
http://www.dfrobot.com/wiki/index.php/LCD_KeyPad_Shield_For_Arduino_SKU:_DFR0009
GPSシールドが無い場合でも、シリアルモニタから直接$GPRMCコマンドを入力する事で時計合わせをする事も可能。
Arudinoの電源を入れるとGPS情報待ちとなり、GPS情報から日時情報が取得出来たらLCDに時計が表示される。
この状態でもJJY信号は発信するが、余分な処理が走り信号が正しく出るかわからないのでLCDシールドのSELECTボタンを押すことでJJY信号発信専念モードと切り替える様にしている。
GPS信号を受信できるところで時計合わせを行い、電波時計のそばでJJY発信モードに切り替え。
電波はあまり飛ばないので、時計の電波状況を見ながらアンテナの位置を調整する。しばらくすると電波時計の時計がArduino表示の時計と同期する。
コードは以下の通り。下記サイトからタイムライブラリも入れておく事。
http://www.pjrc.com/teensy/td_libs_Time.html
csvテキストからパラメータ抜き出す関数は他の用途でも便利に使えるのではないかと思う。
#include <LCD4Bit_mod.h> #include <Time.h> #include <SPI.h> //read gps data from serial, set to time object, show to LCD unit, send JJY signal //press select button on lcd shield to switch GPS receive / JJY Transmit mode //Though JJY trasmit is done while in GPS receive mode, switching to JJY Transmit mode //may give better signal result //http://www.hiramine.com/physicalcomputing/general/gps_nmeaformat.html //http://www.elecrow.com/wiki/index.php?title=GPS_shield //http://d.hatena.ne.jp/NeoCat/20110328/1301256560 //e.g. $GPRMC,025256.00,V,,,,,,,120513,,,N*7F // $GPGGA,025254.00,,,,,0,00,99.99,,,,,,*62 // $GPRMC,225446.00,A,4916.452653,N,12311.123747,W,000.5,054.7,191114,06.2,W,A*68 // $GPGGA,085120.307,3541.1493,N,13945.3994,E,1,08,1.0,6.9,M,35.9,M,,0000*5E #ifndef cbi #define cbi(PORT, BIT) (_SFR_BYTE(PORT) &= ~_BV(BIT)) #endif #ifndef sbi #define sbi(PORT, BIT) (_SFR_BYTE(PORT) |= _BV(BIT)) #endif char junk = ' '; String datain; //data from serial char subch[96]; //sub string used for cvs char chbuff[128]; char *cmd; long gpstime; //gps time info long gpsdate; //gps date info bool gpsstatus; //gps data valid / invalid flag bool initialized; //time initialized int lastsec; //last second updated int lastgpssec; //last gps data received int numgps; static long timezoneadjust = 3600 * 9; //+9 hour from UTC //mode and keypad input int mode; int adc_key_val[5] = { 30, 150, 360, 535, 760 }; int NUM_KEYS = 5; int adc_key_in; int key; int oldkey; //for JJY transmission byte timecode[60]; bool jjyemitting; int jjy_msdur; unsigned long jjy_startTime; LCD4Bit_mod lcd = LCD4Bit_mod(2); void setup() { mode = 0; lcd.init(); lcd.clear(); jjyemitting = false; initialized = false; lastsec = -1; lastgpssec = -1; numgps = 0; Serial.begin(9600); //Serial.println("Waiting for GPS..."); lcd.cursorTo(1, 0); lcd.printIn("---[--]"); } void loop() { if (mode == 0) { //GPS receive mode if (Serial.available()) { while (Serial.available() > 0) { junk = Serial.read(); datain = datain + junk; } if (junk == '\n' || junk == '\r') { //end of line datain.trim(); if (datain.length() > 0) { Serial.println(datain); cmd = getcsvparam(&datain[0], 0); if (strcmp(cmd, "$GPRMC") == 0) { //get time from RMC data if (strlen(getcsvparam(&datain[0], 1)) > 6) { gpstime = (long)atof(getcsvparam(&datain[0], 1)); //time gpsstatus = strcmp(getcsvparam(&datain[0], 2), "A") == 0 ? true : false; //validity gpsdate = atol(getcsvparam(&datain[0], 9)); //date setTime(gpstime / 10000, (gpstime / 100) % 100, gpstime % 100, gpsdate / 10000, (gpsdate / 100) % 100, (gpsdate % 100) + 2000); adjustTime(timezoneadjust); initialized = true; } datain = ""; } else if (strcmp(cmd, "$GPGGA") == 0) { //get satellite number numgps = atoi(getcsvparam(&datain[0], 6)); lastgpssec = second(); datain = ""; } else { datain = ""; } } } } if (initialized) { if (lastsec != second()) { emitJJY(); char gpsinfobuff[5]; if ((second() + 60 - lastgpssec) % 60 > 5) //if over 5 sec since last gps data receive, clear flag lastgpssec = -1; if (lastgpssec == -1) { strcpy(gpsinfobuff, "GPS[--]"); } else { sprintf(gpsinfobuff, "GPS[%02d]", numgps); } if (gpsstatus) { //sprintf(chbuff, "GPS[%s]:%02d:%02d:%02d %04d/%02d/%02d", gpsinfobuff, hour(), minute(), second(), year(), month(), day()); //Serial.println(chbuff); lcd.cursorTo(1, 0); lcd.printIn(gpsinfobuff); sprintf(chbuff, "%02d:%02d:%02d %02d-%02d", hour(), minute(), second(), month(), day()); lcd.cursorTo(2, 0); lcd.printIn(chbuff); } else { //sprintf(chbuff, "---[--]:%02d:%02d:%02d %04d/%02d/%02d", hour(), minute(), second(), year(), month(), day()); //Serial.println(chbuff); lcd.cursorTo(1, 0); lcd.printIn("---[--]"); sprintf(chbuff, "%02d:%02d:%02d %02d-%02d", hour(), minute(), second(), month(), day()); lcd.cursorTo(2, 0); lcd.printIn(chbuff); } lastsec = second(); } } } else { //JJY transmit mode if (initialized) { if (lastsec != second()) { emitJJY(); sprintf(chbuff, "%02d:%02d:%02d %02d-%02d", hour(), minute(), second(), month(), day()); lcd.cursorTo(2, 0); lcd.printIn(chbuff); while (jjyemitting) procJJY(); lastsec = second(); } } else { lcd.cursorTo(1, 0); lcd.printIn("Time not synced"); } } checkkey(); procJJY(); //to stop emission //do not put delay in loop or serial data will get lost (only 62 bytes will be read) } //return n-th char array from csv text char *getcsvparam(char *cvstxt, int index) { int strcnt = 0; int csvcnt = 0; memset(subch, 0, 96); for (int i = 0; i < strlen(cvstxt); i++) { if (cvstxt[i] == ',') { csvcnt++; if (csvcnt > index) break; } else if (csvcnt == index) { subch[strcnt++] = cvstxt[i]; } } return &subch[0]; } void checkkey() { adc_key_in = analogRead(0); // read the value from the sensor key = get_key(adc_key_in); // convert into key press if (key != oldkey) // if keypress is detected { delay(50); // wait for debounce time adc_key_in = analogRead(0); // read the value from the sensor key = get_key(adc_key_in); // convert into key press if (key != oldkey) { oldkey = key; if (key == 4) { //select key lcd.clear(); if (mode == 0) { mode = 1; //JJY transmit mode lcd.cursorTo(1,0); lcd.printIn("JJY Transmit"); } else { mode = 0; //GPS receive mode //flush any incoming data before switching if (Serial.available()) { while (Serial.available() > 0) { junk = Serial.read(); } } lcd.cursorTo(1,0); lcd.printIn("---[--]"); } } } } } int get_key(unsigned int input) { int k; for (k = 0; k < NUM_KEYS; k++) { if (input < adc_key_val[k]) { return k; } } if (k >= NUM_KEYS) k = -1; // No valid key pressed return k; } //JJY signal void emitJJY() { if (jjyemitting) return; jjy_startTime = millis(); // generate 40khz from 3 pin using PWM pinMode(3, OUTPUT); digitalWrite(3, LOW); TCCR2A = _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS20); OCR2A = F_CPU / 2 / 40000/*hz*/; OCR2B = OCR2A / 2; /* 50% duty */ sbi(TCCR2A, COM2B1); // calc signal duration (ms) jjy_msdur = calcTimeCodeDuration(); jjyemitting = true; lcd.cursorTo(1, 15); lcd.printIn("*"); } void procJJY() { if (jjyemitting) { if (millis() - jjy_startTime >= jjy_msdur) { cbi(TCCR2A, COM2B1); jjyemitting = false; lcd.cursorTo(1, 15); lcd.printIn(" "); } } } unsigned int calcTimeCodeDuration() { int s = second(); if (s == 0) setupTimeCode(); return timecode[s] * 100; } void setupTimeCode() { int i; memset(timecode, 8, sizeof(timecode)); setupTimeCode100(minute(), 0); timecode[0] = 2; setupTimeCode100(hour(), 10); int d = dayOfYear(); setupTimeCode100(d / 10, 20); setupTimeCode100(d % 10 * 10, 30); int parity1 = 0, parity2 = 0; for (i = 12; i < 20; i++) parity1 ^= timecode[i] == 5; for (i = 1; i < 10; i++) parity2 ^= timecode[i] == 5; timecode[36] = parity1 ? 5 : 8; timecode[37] = parity2 ? 5 : 8; setupTimeCode100(year() % 100, 40); for (i = 44; i > 40; i--) timecode[i] = timecode[i - 1]; timecode[40] = 8; int w = weekday() - 1; timecode[50] = (w & 4) ? 5 : 8; timecode[51] = (w & 2) ? 5 : 8; timecode[52] = (w & 1) ? 5 : 8; timecode[59] = 2; } void setupTimeCode100(int m, int i) { timecode[i + 0] = ((m / 10) & 8) ? 5 : 8; timecode[i + 1] = ((m / 10) & 4) ? 5 : 8; timecode[i + 2] = ((m / 10) & 2) ? 5 : 8; timecode[i + 3] = ((m / 10) & 1) ? 5 : 8; timecode[i + 4] = 8; timecode[i + 5] = ((m % 10) & 8) ? 5 : 8; timecode[i + 6] = ((m % 10) & 4) ? 5 : 8; timecode[i + 7] = ((m % 10) & 2) ? 5 : 8; timecode[i + 8] = ((m % 10) & 1) ? 5 : 8; timecode[i + 9] = 2; } int dayOfYear() { tmElements_t tm = {0, 0, 0, 0, 1, 1, CalendarYrToTm(year())}; time_t t = makeTime(tm); return (now() - t) / SECS_PER_DAY + 1; }
初めまして、同じGPSシールドで試しています、このようなエラーを吐いてコンパイルできませんでした
対策を教えて頂けたら助かります、
Arduino: 1.6.4 (Windows 7), ボード:”Arduino Uno”
C:\Users\axion.tk\Documents\Arduino\libraries\LCD4Bit_mod\LCD4Bit_mod.cpp:29:57: fatal error: WConstants.h: No such file or directory
#include “WConstants.h” //all things wiring / arduino
^
compilation terminated.
コンパイル時にエラーが発生しました。
This report would have more information with
“コンパイル中の詳細な出力を表示する”
ファイル > 設定 で有効にする
返信遅くなりましたが
#include “WConstants.h”
を
#include <Arduino.h>
にしてみて下さい。
色々試しましたが#include でコンパイルが通り作動しました、有難うございます。