[아두이노] GPS 모듈의 사용 1편 - GPS 로그 찍어보기
아두이노에 GPS 모듈을 연결하면 자신의 현재위치 등의 정보를 알 수 있습니다. GPS 는 Global Positioning System 의 약자로서 미국 국방부에서 띄운 24개 이상의 위성에서 제공 해주는 신호를 이용하여 벡터방식으로 현재의 위도와 경도를 알 수 있도록 하는 시스템 입니다. 보통 GPS 신호는 실내에서는 잡기 힘들며 하늘이 보이는 야외로 나가야 잡을 수 있습니다. 그리고 3개 이상의 위성의 신호가 잡혀야 벡터방식으로 현재의 위치를 구할 수 있습니다. 많은 위성이 일정한 간격으로 지구전체를 돌고 있으므로 지구상 어느곳 에서든지 항상 3개 이상의 GPS 신호를 잡을 수 있습니다. 고로 자동차의 네비게이션, 조난 구조장치, 선박의 항법장치, 항공기 자동 조종장치 등 수 많은 산업에 쓰이고 있습니다. 이런 GPS 를 이용할 수 있는 모듈을 아두이노에 활용하여 보도록 하겠습니다.
전세계를 동시에 Cover 하고 있는 GPS 위성들 (어떠한 지역에서도 6 개 이상의 위성이 항상 신호를 보내주고 있습니다.)
GPS 모듈의 모습입니다. 왼쪽이 GPS 모듈이고 오른쪽은 안테나 입니다.
GPS 모듈의 이름은 GY-NEO6MV2 입니다. 메인칩은 UBlox 사의 NEO-6M-0-001 입니다. 기본적인 스펙은 다음과 같습니다.
■ 데이터시트 :
GPS 모듈에는 PIN 이 없습니다. 그래서 아두이노 와의 연결을 위해서는 납땜을 해야 합니다. 4개의 암/수 케이블의 암 부분을 잘라내고 GPS 모듈에 납땜을 하려고 합니다.
모듈에는 4개의 단자가 있습니다. 전원공급을 위한 VCC (3.3V 이나 5V 모두 연결 가능) , GND , RX , TX 가 있습니다. RX 와 TX 로 시리얼통신을 통해서 신호를 주고 받게 됩니다. 4개의 선을 납땜해 주었습니다.
모듈에 안테나를 연결하고 반대쪽 숫놈 단자를 아두이노에 연결해 주었습니다. 안테나는 꼽을 수 있도록 커넥터가 마련되어 있어서 그냥 꼽으면 됩니다. 아두이노와의 연결은 다음과 같이 해 주었습니다.
GPS 모듈 | 아두이노 |
VCC | 5V |
GND | GND |
RX | D5 |
TX | D6 |
밖으로 나가야 GPS 신호를 잡을 수 있으므로 노트북과 모듈, 아두이노를 챙겨서 의왕시에 있는 최근에 개장한 왕송호수공원에 나가 보았습니다. 늦봄의 정취가 참 아름답습니다. ^^
적당한 벤치에 앉아 노트북을 펼치고 GPS 모듈이 연결된 아두이노를 연결하고 준비한 소스를 업로드 합니다.
소스 1
이 소스는 GPS 가 잘 잡히는지 테스트용 입니다. GPS 모듈에서 보내주는 내용을 그냥 찍어보는 용도 입니다. 그래도 들어 있을 정보는 모두 들어 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <SoftwareSerial.h> SoftwareSerial gpsSerial(6,5); void setup() { Serial.begin(9600); Serial.println("Start GPS... "); gpsSerial.begin(9600); } void loop() { if(gpsSerial.available()) { Serial.write(gpsSerial.read()); } } | cs |
시리얼 모니터에 다음과 같이 데이터가 뜹니다. 계속 뜹니다.
1 2 3 4 5 6 7 8 9 | $GPRMC,082608.00,A,3730.67773,N,12694.42632,E,0.048,,110516,,,D*78 $GPVTG,,T,,M,0.048,N,0.089,K,D*2B $GPGGA,082608.00,3730.67773,N,12694.42632,E,2,08,1.54,49.8,M,18.5,M,,0000*6D $GPGSA,A,3,08,42,01,07,11,16,50,10,,,,,2.95,1.54,2.51*01 $GPGSV,3,1,09,01,44,188,40,07,57,267,41,08,64,024,45,10,12,063,36*7A $GPGSV,3,2,09,11,66,211,45,16,23,108,34,27,,,28,42,43,152,37*46 $GPGSV,3,3,09,50,43,152,38*4F $GPGLL,3730.67773,N,12694.42632,E,082608.00,A,D*6F | cs |
무언가 알 수 없는 데이터가 계속 0.5초 간격으로 시리얼 모니터에 표시가 되는데... 이건 NMEA 이라고 부르는 GPS 규약에 의한 프로토콜 이라고 합니다. 미국의 The National Marine Electronics Association 이라는 기관에서 정한 프로토콜 이라고 하는데 자세한 내용은 아래의 링크에서 볼 수 있습니다.
■ NMEA 자세히 알아보기 : http://www.gpsinformation.org/dale/nmea.htm
NMEA에 대해서는 간략히 정리된 표를 하나 참고삼아 가져와 보겠습니다.
Name | Garmin | Magellan | Lowrance | SiRF | Notes: |
GPAPB | N | Y | Y | N | Auto Pilot B |
GPBOD | Y | N | N | N | bearing, origin to destination - earlier G-12's do not transmit this |
GPGGA | Y | Y | Y | Y | fix data |
GPGLL | Y | Y | Y | Y | Lat/Lon data - earlier G-12's do not transmit this |
GPGSA | Y | Y | Y | Y | overall satellite reception data, missing on some Garmin models |
GPGSV | Y | Y | Y | Y | detailed satellite data, missing on some Garmin models |
GPRMB | Y | Y | Y | N | minimum recommended data when following a route |
GPRMC | Y | Y | Y | Y | minimum recommended data |
GPRTE | Y | U | U | N | route data, only when there is an active route. (this is sometimes bidirectional) |
GPWPL | Y | Y | U | N | waypoint data, only when there is an active route (this is sometimes bidirectional) |
▲ NMEA 2.0 프로토콜
위의 표를 보니 제가 알고싶어하는 데이터는 현재의 위도와 경도 데이터 입니다. 즉 GPGLL 에 있는 값인것 같습니다. 위도와 경도로 보이는 데이터가 여러군데에 보이는데 확실한 차이는 잘 모르겠습니다. 혹시 이 글을 보시는 분들 중에 차이점 아시는 분은 댓글로 알려주시면 감사하겠습니다. GPGGA 가 Fix Data 인것 보면 더 정확한 값인것 같기도 하구요. 아무튼 GPGLL 에 들어있는 위도와 경도값으로 보이는 3730.67773,N,12694.42632,E 라는 값을 그대로 구글맵에서 검색해 보았습니다. 찾을 수 없다고 나옵니다. 위도와 경도값은 맞는데 소숫점을 우리가 알고 있는 위도와 경도 단위로 바꿔 보았습니다. 37.3067773, 126.9442632 로 수정해서 검색해 보았습니다.
이제야 제대로 나옵니다. 제가 앉아있던 벤치가 있던 자리가 정확히 맞습니다. 뭐 스마트폰 GPS로도 얼마든지 알 수 있는 기능이지만 GPS 의 RAW 데이터를 분석해서 현재 위치를 알아보니 뭔가 신기하기도 하고 그렇습니다.
하지만 위의 NMEA 데이터를 보시면 알겠지만 일반인이 알아보기에 굉장히 어렵습니다. 이를 Parsing 해야 데이터로서 가치가 더욱 높아질 것 같습니다.
소스 2
인터넷을 뒤져보니 TinyGPS 라는 라이브러리가 나옵니다. GPS RAW 데이터를 손쉽게 Parsing 해서 활용할 수 있도록 하는 라이브러리 입니다. 이를 사용해 보겠습니다.
■ TinyGPS 라이브러리
https://github.com/mikalhart/TinyGPS
위에서 다운로드 받은 라이브러리를 설치하고 아래의 예제 소스를 실행해 보았습니다. 실행 후 15분 정도를 걸으면서 GPS 로그를 10초 마다 기록해 보았습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #include <SoftwareSerial.h> #include <TinyGPS.h> // Define which pins you will use on the Arduino to communicate with your // GPS. In this case, the GPS module's TX pin will connect to the // Arduino's RXPIN which is pin 3. #define RXPIN 6 #define TXPIN 5 //Set this value equal to the baud rate of your GPS #define GPSBAUD 9600 // Create an instance of the TinyGPS object TinyGPS gps; // Initialize the NewSoftSerial library to the pins you defined above SoftwareSerial uart_gps(RXPIN, TXPIN); // This is where you declare prototypes for the functions that will be // using the TinyGPS library. void getgps(TinyGPS &gps); // In the setup function, you need to initialize two serial ports; the // standard hardware serial port (Serial()) to communicate with your // terminal program an another serial port (NewSoftSerial()) for your // GPS. void setup() { // This is the serial rate for your terminal program. It must be this // fast because we need to print everything before a new sentence // comes in. If you slow it down, the messages might not be valid and // you will likely get checksum errors. Serial.begin(9600); //Sets baud rate of your GPS uart_gps.begin(GPSBAUD); Serial.println(""); Serial.println("GPS Shield QuickStart Example Sketch v12"); Serial.println(" ...waiting for lock... "); Serial.println(""); } // This is the main loop of the code. All it does is check for data on // the RX pin of the ardiuno, makes sure the data is valid NMEA sentences, // then jumps to the getgps() function. void loop() { while(uart_gps.available()) // While there is data on the RX pin... { int c = uart_gps.read(); // load the data into a variable... if(gps.encode(c)) // if there is a new valid sentence... { getgps(gps); // then grab the data. } } } // The getgps function will get and print the values we want. void getgps(TinyGPS &gps) { // To get all of the data into varialbes that you can use in your code, // all you need to do is define variables and query the object for the // data. To see the complete list of functions see keywords.txt file in // the TinyGPS and NewSoftSerial libs. // Define the variables that will be used float latitude, longitude; // Then call this function gps.f_get_position(&latitude, &longitude); // You can now print variables latitude and longitude Serial.print("Lat/Long: "); Serial.print(latitude,5); Serial.print(", "); Serial.println(longitude,5); // Same goes for date and time int year; byte month, day, hour, minute, second, hundredths; gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths); // Print data and time Serial.print("Date: "); Serial.print(month, DEC); Serial.print("/"); Serial.print(day, DEC); Serial.print("/"); Serial.print(year); Serial.print(" Time: "); Serial.print(hour, DEC); Serial.print(":"); Serial.print(minute, DEC); Serial.print(":"); Serial.print(second, DEC); Serial.print("."); Serial.println(hundredths, DEC); //Since month, day, hour, minute, second, and hundr // Here you can print the altitude and course values directly since // there is only one value for the function Serial.print("Altitude (meters): "); Serial.println(gps.f_altitude()); // Same goes for course Serial.print("Course (degrees): "); Serial.println(gps.f_course()); // And same goes for speed Serial.print("Speed(kmph): "); Serial.println(gps.f_speed_kmph()); Serial.println(); // Here you can print statistics on the sentences. unsigned long chars; unsigned short sentences, failed_checksum; gps.stats(&chars, &sentences, &failed_checksum); //Serial.print("Failed Checksums: ");Serial.print(failed_checksum); //Serial.println(); Serial.println(); delay(10000); } | cs |
위의 소스에 자세하게 영어로 주석이 쓰여 있으니 참고하시면 되겠습니다. 사실 자세히 보면 주석만 많지 별거 없는 소스 입니다. 맨 아래 delay 를 빼버리면 GPS 에서 주는대로 데이터를 계속 받습니다.
1 2 3 4 5 6 | Lat/Long: 37.30678, 126.94424 Date: 5/11/2016 Time: 8:41:30.0 Altitude (meters): 1000000.00 Course (degrees): 0.00 Speed(kmph): 0.09 | cs |
로그는 위와 같이 찍힙니다. 위의 RAW 데이터 보다 훨씬 보기가 쉽습니다. 일단 첫째줄에 위도 경도가 나오고 날짜, 시간, 고도(이건 오류인듯 합니다. 계속 저렇게 나오네요.), 진행방향, 움직이는 속도가 찍힙니다.
모든 데이터가 보고 싶은 분들은 아래 파일을 다운로드 받으세요.
이 파일을 GPS 로그 파일인 gpx 파일로 변환해서 구글어스에서 불러와 보았습니다. gpx 파일로의 변환은 Notepad++ 의 매크로 기능을 이용하니 간단하게 되네요. 어차피 gpx 파일이 xml 형식의 파일이기 때문에 형식만 맞춰서 파일은 만들어주면 됩니다. 나중에 GPS Logger 도 만들어볼 생각인데 그 때는 gpx 파일형식으로 바로 저장하도록 만들어야겠죠. 제가 만든 gpx 파일은 아래 파일을 다운로드 받아 보시면 됩니다.
gpx 파일을 구글어스를 실행하고 열기->gpx 파일을 선택해서 열어주시면 다음과 같이 움직인 GPS 로그기록을 볼 수 있습니다.
파란색으로 제가 움직인 구간이 표시 됩니다. 15분 동안 꽤 많이 걸었네요.
이상 몸으로 체험해본 아두이노 GPS 모듈 사용기 였습니다.
다음 계획은 GPS 로그 기록을 SD카드 저장 모듈을 통해서 마이크로SD 카드에 저장해 보는 것 입니다. 그렇게 하면 첩보영화에서 가끔 나오는 차량에 자석으로 붙이는 위치 추적기? 비슷한 것을 만들어 볼 수 있을 것입니다.
미드 브레이킹 배드에서 나왔던 GPS 추적기
출처: https://deneb21.tistory.com/331 [Do It Yourself!]