안녕하세요!
커피백작입니다!
음... 먼저, 제가 블로그 시작한지 얼마 안된 초짜라, 업로드할때 '어디서 샀는지, 어디글을 가져다 써도 되는지 등등' 저작권문제등은 잘 모르겠는데요
일단 제가 실제로 구매해서 사용한 제품으로, 직접 사진찍어 올리며,
여기저기서 자료 찾아서 직접 돌려본 소스코드를 올리는 것을 말씀드리며, 일정글을 퍼오면 명시해 남길 것입니다.
혹 문제된다면 알려주세요.
자, 그럼 시작하죠
오늘 업로드 내용은 GPS 모듈입니다!
GY-GPS6MV2 라고 써있는 제품인데요.
부가세 포함 해서 13,200 원 정도에 샀네요.
시리얼 통신을 하는 녀석이구요.
테스트는 아두이노 우노보드에 연결해서 사용했습니다.
선은 VCC, RX, TX, GND 로 4개의 선을 연결하여 사용합니다.
VCC 와 GND 는 전원 부분이고,
RX, TX 는 시리얼 통신을 담당하는 부분이래요.(저도 잘 몰라욬ㅋㅋㅋ 그렇지만 다음에 파고 들어서 업로드하죠)
// gps - arduino
// tx - 10
// rx - 11
// vcc - 5v
// gnd - gnd
선의 연결입니다. GPS 모듈과 우노보드를 연결했을때 핀 번호들인데요.
(아시는 분이 많겠지만, 모듈과 보드들의 RX, TX 는 서로 교차 시켜줘야 합니다.
저도 예전엔 몰라서 TX-TX RX-RX 서로 맞춰서 연결했었는데, 찾다보니 교차 연결해야 된다하더군요.
저처럼 모르셨을 수 있는 분들 위해 남깁니다)
바로 한번 연결해보겠습니다.
자 이렇게 연결을 해 놓은 상태로 진행하겠습니다.
아두이노 스케치에 처음 업로드 할 소스는 다음과 같습니다.
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(10,11);
// gps - arduino
// tx - 10
// rx - 11
// vcc - 5v
// gnd - gnd
void setup() {
Serial.begin(9600);
Serial.println("Start GPS... ");
gpsSerial.begin(9600);
}
void loop() {
if(gpsSerial.available())
{
Serial.write(gpsSerial.read());
}
}
해당 제품에 이상태로 업로드를 합시다!
업로드 후 시리얼 모니터를 열면 위의 사진과 같이 값이 주르르르르르르르르르르 나와요.
이 값에서 뭐가 좌표일지, 막막하지 않나요? ㅋㅋㅋ
단순히 TinyGPS 같은 라이브러리를 추가해서 불러옴으로 원하는 값만 출력할 수 도 있습니다만.
저는 그러기 싫었기에 이 기본소스에서 계속 바꿔 보았습니다.
현재는 시리얼 모니터에 보면 Start GPS 를 출력한 다음
$ 표시와 함께 각각 줄 앞에 5글자와 그 뒤를 이어 각각에 따른 값들이 계속 반복해서 출력합니다.
NMEA 라고 하는 프로토콜인데요.
이 분 링크에 가셔서 보시면 잘 정리 되어있어용.
이 NMEA에 대한 것은 링크 통해 보시구요.
저희가 GPS 모듈에서 가져올 값은 위도Lat 과 경도Long 잖습니까?
시리얼 모니터에 반복적으로 출력되는 문장들중, "GPGGA"의 값을 봐주세요.
GPGGA 에서 빨간색으로 밑줄 그어진 부분이 바로 위도와 경도입니다.
N 앞에 있는 값이 위도.
E 앞에 있는 값이 경도입니다.
저희는 그 값을 가져와야 합니다.
그.런.데.
그 부분의 값을 그대로 가져온다고해서 다 되는게 아니더라구요 하하
제가 있는 곳이 순천인데 고흥이 나오지 뭡니까~
그래서 따로 계산을 해줘야 정확한 위치 값이 나왔습니다.
계산식은 의외로 간단했습니다.
$GPGGA,015442.00, 3458.17997 ,N, 12728.74791 ,E,1,04,6.67,39.9,M,21.1,M,,*61
3458.17997 => 34+58.17997/60 -> 34.9696661667
12728.74791 => 127 + 28.74791/60 -> 127.4791318333
GPGGA 에서 나온 값을 위와 같이 계산해주면됩니다.
위도는 앞 두자리.
경도는 앞 세자리.
따로 가져다가 더해주면 됩니다.(뒤에있는 값들을 60으로 나눈 값에 더해야지요!)
이런 계산식을 돌리기위해서,
GPGGA 값을 따로 가져와야겠지요?
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(10,11);
// gps - arduino
// tx - 10
// rx - 11
// vcc - 5v
// gnd - gnd
char c = "";
String str = "";
String targetStr = "GPGGA";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("Start GPS... ");
gpsSerial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
if(gpsSerial.available())
{
c=gpsSerial.read();
if(c == '\n'){
if(targetStr.equals(str.substring(1, 6))){
Serial.println(str);
}
str = "";
}else{
str += c;
}
}
}
소스는 위와 같습니다.
이렇게 하면 아래와 같은 모습인데용
일단 GPGGA 의 값만을 계속 가져오지요?
설명을 드리자면,
먼저 모듈에서 읽어온 값들이 단어 하나씩을 gpsSerial.read()로 읽어와 char 데이터형의 c 변수에 저장합니다.
그리고 이 c의 값이 차곡차곡 str 이라는 변수에 저장되는데, 만약 읽어온 c 의 값이 \n 인 줄바꿈 문자라면.
str.substring(1,6) 을 사용하여 $ 다음의 위치에 있는 ID 값을 가져와 targetStr인 GPGGA 와 비교합니다.
그리고 읽어온 str 이 GPGGA 라면? str을 출력해 주는 거죠!
그 이후 반드시 str = "" 로 str 값을 초기화 해주어야합니다
(만약 .substring() 이 무엇인 지 모르시겠다 하시면 다음의 링크로 들어가 보세요
https://kocoafab.cc/tutorial/view/115
위 링크에 아두이노의 string 형으로 할 수 있는 기능을 자세히 설명해 주셨습니다.
저도 덕분에 어떻게 만들어 볼 수 있었죠)
이렇게 GPGGA 의 값을 읽어 왔다면.
이 GPGGA 에서 위도와 경도 값만을 불러 와야 하지 않겠나요?
$GPGGA,015442.00,3458.17997,N,12728.74791,E,1,04,6.67,39.9,M,21.1,M,,*61
GPGGA 의 값들을 자세히 보시면 ',' 쉼표로 구분지어져 있다는 것을 아실거예요.
로직을 짜다보면 문자열을 잘라서 일부분만 쓰는 경우가 많은데요~
이때 쪼개는 작업을 '파싱(Parsing)' 이라해요.
근데 어떻게 잘라 쓸 수 있을까요?
GPGGA 의 예를 들면, 쉼표로 구분 지어서 값을 가져올 수 있지 않겠습니까?
이 때 쉼표 처럼 구분지어 주는 문자열이나 문자를 구분자 라고해요.
(토큰이라는 말도 있는데, 토큰은 전체 문자열을 구분자로 쪼갠 단위 입니다)
GPGGA 의 값을 쉼표로 자르기 위해서 코코아팹 링크에도 나와있는 indexOf 를 사용하였습니다.
실제 적용 소스를 보면서 이해를 해볼까요?
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(10,11);
// gps - arduino
// tx - 10
// rx - 11
// vcc - 5v
// gnd - gnd
char c = ""; // Wn 인지 구분 및 str에 저장.
String str = ""; // \n 전까지 c 값을 저장.
String targetStr = "GPGGA"; // str의 값이 NMEA의 GPGGA 값인지 타겟
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("Start GPS... ");
gpsSerial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
if(gpsSerial.available()) // gps 센서 통신 가능?
{
c=gpsSerial.read(); // 센서의 값 읽기
if(c == '\n'){ // \n 값인지 구분.
// \n 일시. 지금까지 저장된 str 값이 targetStr과 맞는지 구분
if(targetStr.equals(str.substring(1, 6))){
// NMEA 의 GPGGA 값일시
Serial.println(str);
// , 를 토큰으로서 파싱.
int first = str.indexOf(",");
int two = str.indexOf(",", first+1);
int three = str.indexOf(",", two+1);
int four = str.indexOf(",", three+1);
int five = str.indexOf(",", four+1);
// Lat과 Long 위치에 있는 값들을 index로 추출
String Lat = str.substring(two+1, three);
String Long = str.substring(four+1, five);
// Lat의 앞값과 뒷값을 구분
String Lat1 = Lat.substring(0, 2);
String Lat2 = Lat.substring(2);
// Long의 앞값과 뒷값을 구분
String Long1 = Long.substring(0, 3);
String Long2 = Long.substring(3);
// 좌표 계산.
double LatF = Lat1.toDouble() + Lat2.toDouble()/60;
float LongF = Long1.toFloat() + Long2.toFloat()/60;
// 좌표 출력.
Serial.print("Lat : ");
Serial.println(LatF, 15);
Serial.print("Long : ");
Serial.println(LongF, 15);
}
// str 값 초기화
str = "";
}else{ // \n 아닐시, str에 문자를 계속 더하기
str += c;
}
}
}
GPGGA 가 저장된 str 에서 쉼표로 구분하여 값을 자릅니다.
그리고 위도와 경도를 각각 Lat 과 Long 에 저장한 후
또다시 나누어 계산한 뒤 Lat과 Long 에 저장, 출력합니다.
이로써 계산된 위도와 경도 값을 구해왔어요~
GPS 모듈을 이용해 보았는데요~
조금이라도 도움이 되었으면 좋겠습니다~!