ArduinoでGPSモジュールの内容をもっと表示させる
TOP > てきとうにこらむ > ゲーム作りとプログラミング日記 > ArduinoでGPSモジュールの内容をもっと表示させる
GPSには色々な情報がある
GPSは座標だけではない。座標を取得するためには正確な時刻が必要だし。
スイッチを使って切り替えてみる
そうしたらどうしようか。電源を投入したらはじめは座標を表示させて、ボタンを押したら時間を表示させてみよう。
コード
参考にしたコード
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
#define READBUFFERSIZE (256)
#define DELIMITER (",")
const int POSITIONMODE = 1;
const int TIMEMODE = 2;
const int BUTTON = 8;
SoftwareSerial g_gps( 6, 7 );
LiquidCrystal g_lcd( 11, 12, 2, 3, 4, 5 );
char g_szReadBuffer[READBUFFERSIZE] = "";
int g_iIndexCounter = 0;
void setup()
{
g_gps.begin( 9600 );
g_lcd.begin( 16, 2 );
g_lcd.clear();
g_lcd.setCursor( 0, 0 );
g_lcd.print( "unko" );
Serial.begin( 9600 );
pinMode(BUTTON, INPUT);
}
int analyzePosition ( char szLineString[], float &rfLat, float &rfLong )
{
rfLat = 0.0;
rfLong = 0.0;
if ( 0 != strncmp( "$GPRMC", szLineString, 6 ) )
{
return 0;
}
strtok( szLineString, DELIMITER );
strtok( NULL, DELIMITER );
strtok( NULL, DELIMITER );
char* pszLat = strtok( NULL, DELIMITER );
strtok( NULL, DELIMITER );
char* pszLong = strtok( NULL, DELIMITER );
if (NULL == pszLong )
{
return 0;
}
float temp, deg, min;
temp = atof(pszLat);
deg = (int)(temp/100);
min = temp - deg * 100;
rfLat = deg + min / 60;
temp = atof(pszLong);
deg = (int)(temp/100);
min = temp - deg * 100;
rfLong = deg + min / 60;
return 1;
}
int analyzeTIME( char szLineString[], int& hour, int& minutes, int& seconds )
{
hour = 0;
minutes = 0;
seconds = 0;
if ( 0 != strncmp( "$GPRMC", szLineString, 6 ) )
{
return 0;
}
strtok( szLineString, DELIMITER );
char *hms = strtok( NULL, DELIMITER );
char temp[3];
temp[2] = '\\0';
strncpy( temp, hms + 0, 2 );
hour = atoi( temp );
strncpy( temp, hms + 2, 2 );
minutes = atoi( temp );
strncpy( temp, hms + 4, 2 );
seconds = atoi( temp );
return 1;
}
int readLineString( SoftwareSerial& serial,
char szReadBuffer[], const int ciReadBufferSize, int& riIndexChar,
char szLineString[], const int ciLineStringSize )
{
char c;
while ( ( c = serial.read() ) != -1 )
{
switch ( c )
{
case '\\r':
szReadBuffer[riIndexChar] = '\\0';
strncpy( szLineString, szReadBuffer, ciLineStringSize - 1 );
szLineString[ciLineStringSize - 1] = '\\0';
riIndexChar = 0;
return 1;
case '\\n':
case -1:
break;
default:
if ( (ciReadBufferSize - 1) > riIndexChar )
{
szReadBuffer[riIndexChar] = c;
riIndexChar++;
}
}
}
return 0;
}
void selectPrintGPSMode(char *szLineString, int mode)
{
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minutes = 0;
int seconds = 0;
float fLat = 0.0;
float fLong = 0.0;
switch( mode ) {
case POSITIONMODE:
if ( analyzePosition( szLineString, fLat, fLong ) )
{
printPosition(fLat, fLong);
}
break;
case TIMEMODE:
if ( analyzeTIME( szLineString, hour, minutes, seconds ) )
{
printTime( hour, minutes, seconds );
}
break;
default:
break;
}
}
void printPosition(float fLat, float fLong)
{
char szLat[11];
char szLong[11];
char szBuffer[17];
dtostrf(fLat, 10, 6, szLat);
dtostrf(fLong, 10, 6, szLong);
snprintf( szBuffer, 17, "Lat. :%s", szLat );
g_lcd.setCursor( 0, 0 );
g_lcd.print( szBuffer );
snprintf( szBuffer, 17, "Long.:%s", szLong );
g_lcd.setCursor( 0, 1 );
g_lcd.print( szBuffer );
}
void printTime(int hour, int minutes, int seconds)
{
char szBuffer[17];
snprintf( szBuffer, 17, "UTC. : %02d:%02d:%02d", hour, minutes, seconds );
g_lcd.setCursor( 0, 0 );
g_lcd.print( szBuffer );
snprintf( szBuffer, 17, "JST. : %02d:%02d:%02d", ( hour + 9 ) % 24, minutes, seconds );
g_lcd.setCursor( 0, 1 );
g_lcd.print( szBuffer );
}
int isPushed()
{
static int old_val = 0, state = 0;
static unsigned long wait = millis();
int val;
val = digitalRead(BUTTON);
if ( ( val == HIGH ) && ( old_val == LOW ) && ( millis() - wait ) > 10 )
{
state = 1 - state;
wait = millis();
}
old_val = val;
return state;
}
int changeGPSMode(int state)
{
switch( state )
{
case 0:
return POSITIONMODE;
case 1:
return TIMEMODE;
}
return 0;
}
void loop()
{
char szLineString[READBUFFERSIZE];
int mode = 0, gpsMode = 0;
mode = isPushed();
if ( !readLineString( g_gps,
g_szReadBuffer, READBUFFERSIZE, g_iIndexCounter,
szLineString, READBUFFERSIZE ) )
{
return;
}
gpsMode = changeGPSMode( mode );
selectPrintGPSMode( szLineString, gpsMode );
}
これで、ある程度正確なGPS時計としても使えるようになった。
ちなみになんだけど、loop関数で行っていたLCDへの表示を関数へ持って行ったら、表示がバグった。
これは一体なんだろうと思ったら、charで定義されている配列のサイズが16byteしかなかったからだったようだ。16文字表示させたい場合には、charの最後にはnull文字が入るから、char str[17]となる必要があったのだ。
また、sprintfを使用するよりは、snprintfを使って文字数を制限しないと、やはりnull文字が入らず、バグのきっかけになりやすい。ArduinoのスケッチはほとんどCやC++のようだから、文字列の扱いには結構気を配らないと行けないなぁと感じる。