nngh 发表于 2013-3-26 11:04:25

简易GPS,本人第一个作品

本帖最后由 nngh 于 2013-3-26 12:24 编辑

   
很久以前就对Arduino产生兴趣,在论坛潜水很久了,新近入手一块Arduino UNO R3,一块LCD1602 Keypad Shield和一个GPS Shield,上Google搜索了一下,找到一个葡萄牙人编写的GPS解码程序,稍微修改了一下,增加了串口输出数据功能。
   然后,一个简易GPS诞生了,目前只有日期,时间和地点,速度显示功能而已。本人第一个作品。Arduino IDE 1.01编译通过。
代码如下:

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#define DADOS_LEN 100
#define IDLEN 6
#define TEMPLEN 11
#define GPRMC 0
#define GPGGA 1
char data; //buffer for GPS data
byte conta=0; //variavel auxiliar para contar
char* idnmea[] = {"$GPRMC","$GPGGA"}; //IDs dos NMEA que vou utilizar
byte verificador[]= {0,0}; //variavel auxiliar para verificar ID NMEA
byte indice; //Em uma linha $GPRMC contem 12 valores separados por virgulas. Esta variavel guarda a posição do caracter de inicio de cada valor.
byte contindice=0; //variavel auxiliar de controle usada na variavel indice[];
byte menu=0; // Menu do LCD: 0-key RIGHT, 1-key UP, 2-key DOWN, 3-key LEFT, 4-key SELECT
char tempmsg; //variavel temporaria auxiliar para guarda o valor de um dado extraido do GPS.
SoftwareSerial nss(3, 2);
LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
intadc_key_val ={30, 150, 360, 535, 760 }; //valores do divisor de tensão do teclado do LCD Shield
#define NUM_KEYS 5 //numero de teclas do teclado
int adc_key_in; //valor da entrada analogica do teclado
byte key=-1; //tecla pressionada
byte oldkey=-1; //tecla pressionada anteriormente

void setup()
   {
Serial.begin(9600);
nss.begin(9600);
lcd.clear(); //Clear LCD
lcd.begin(16,2);
lcd.print("Arduino.cc");
lcd.setCursor(0, 2); //set cursor on LCD at col 0 and row 2
lcd.print("Arduino GPS");
clearBuffer(); //clear buffer for GPS (databuffer)
clearTemp(); //clear data for GPS (tempmsg)
//Serial.begin(9600); //Inicia UART para comunicar com módulo GPS
   delay(3000);
}

void loop(){   
    while(nss.available()){//if serial port available
      data = nss.read(); //Read a byte of the serial port
      if(data==13){//If the received byte is = to 13, end of transmission
      verificador=0; //verifies idnmea ($GPRMC)
      //verificador=0;//verifies idnmea ($GPGGA)
      for(byte i=1;i<=IDLEN;i++){ //checking the ID NMEA of string received
          if(data==idnmea){ //Verifies that is $GPRMC
            verificador++; //increases 1
          }
         
          //I will not implement $GPGGA do not get too confused
          /*if(data==idnmea){ //Verifies that is $GPGGA
            verificador++; //increases 1
          }*/
      }
      if(verificador==IDLEN){ // if the line received is $GPRMC
          //A line string of GPRMC has 11 "," Divided into 12 data
          //exemplo: $GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70
          //            0      1   2    3    4   5    6    7    8   9   10    11
          // we interesting: 2-timestamp (UTC), 3-latitude, 4-North/South, 5-Longitude, 6-East/West,7-Speed in knots,9-date stamp
          contindice = 0;
          indice = 1; //data[] inicia no caracter 1
          contindice++;
          for(byte i=1; i<DADOS_LEN;i++){ //crosses every line data[] identifying where each value of GPS
            if(data==','){ //found the final of a data
            indice = i+1;
            contindice++;
            }
          }
          adc_key_in = analogRead(0); //verifica entrada analogica do teclado
          key = get_key(adc_key_in); //interpreta valor da entrada analogica
          if (key != oldkey){ //verifica se o valor encontrado é diferente do valor anterior
            delay(50);                // faz um delay para o debounce
            adc_key_in = analogRead(0);   
            key = get_key(adc_key_in);                        // interpreta
            if (key != oldkey){                        //verifica se é diferente
            oldkey = key; //atualiza oldkey
            lcd.clear(); //clear LCD
            if (key >=0){ //se alguma tecla foi pressionada
                menu = key; //atualiza o menu
            }
            }
          }

          switch(menu){
             //show onLCD the key pressed
            case 4: //SELECT 4-key
                lcd.setCursor(1,0); //move cursor to LCDcolumn 1 row 0
                lcd.print("True course"); //print text to LCD
                lcd.setCursor(1,1); //move cursor to LCDcolumn 1 row 1
                lcd.print(datastream(7)); //print text to LCD
            break;            
            case 3: //LEFT 3-key
                lcd.clear();
                lcd.setCursor(0,0);
                lcd.print("Lat:");
                lcd.print(datastream(4));
                lcd.print(" ");
                lcd.print(datastream(3)); // datastream(byte)
                lcd.setCursor(0, 1);
                lcd.print("Lon:");
                lcd.print(datastream(6));
                lcd.print(" ");
                lcd.print(datastream(5));
                break;
            case 1: //UP   1-key
            lcd.setCursor(1,0);
            lcd.print("Date:");
            lcd.print(datastream(9));
            lcd.setCursor(1,1);
            lcd.print("Time:");
            lcd.print(datastream(1));
            break;
            case 2: //DOWN2-key
            lcd.setCursor(1,0);
            lcd.print("Speed:");
            lcd.print(datastream(7));
            lcd.setCursor(1,1);
            lcd.print("knots");
            break;
         }
            Serial.println("---------------");
         for (int i=0;i<=12;i++){
         switch(i){
             case 0 :Serial.print("Time in UTC (HhMmSs): ");Serial.print(datastream(1));break;
             case 1 :Serial.print("Status (A=OK,V=KO): ");Serial.print(datastream(2));break;
             case 2 :Serial.print("Latitude: ");Serial.print(datastream(3));break;
             case 3 :Serial.print("Direction (N/S): ");Serial.print(datastream(4));break;
             case 4 :Serial.print("Longitude: ");Serial.print(datastream(5));break;
             case 5 :Serial.print("Direction (E/W): ");Serial.print(datastream(6));break;
             case 6 :Serial.print("Velocity in knots: ");Serial.print(datastream(7));break;
             case 7 :Serial.print("Heading in degrees: ");Serial.print(datastream(8));break;
             case 8 :Serial.print("Date UTC (DdMmAa): ");Serial.print(datastream(9));break;
             case 9 :Serial.print("Magnetic degrees: ");break;
             case 10 :Serial.print("(E/W): ");break;
             case 11 :Serial.print("Mode: ");break;
             case 12 :Serial.print("Checksum: ");Serial.print(datastream(11));break;
         }
         
         Serial.println("");
         }
         Serial.println("---------------");
      }

      conta = 0; //zero conta, or is, will start next line of GPS and data this at position 0
      clearBuffer(); //clear data[]
      }else{
      conta++; //increases conta, in other words, data skips to the next position
      }
      
    }
}
void clearBuffer(){
for (byte i=0;i<DADOS_LEN;i++){       // clear variavel (buffer) received GPS data
    data=' ';
}
}
void clearTemp(){
for(byte i=0;i<TEMPLEN;i++)
    tempmsg=' ';
}

char* datastream(byte inicio){
/*
Receive Datastream from GPS devices,then convert to Data We can read directly
remenber that: 2-timestamp (UTC), 3-latitude, 4-North/South, 5-Longitude, 6-East/West,7-Speed in knots,9-date stamp
*/
clearTemp();
byte i;
byte fim = indice-2;
inicio = indice;
for(i=0;i<=(fim-inicio);i++){
    tempmsg = data;
}
tempmsg = '\0';
return tempmsg;
}

// Convert ADC value to key number
byte get_key(unsigned int input)
{
int k=menu;
for (byte i = 0; i < NUM_KEYS; i++){
    if (input < adc_key_val){
      k=i;
      return k;
    }
}
return k;

}


对于以上基本功能,也试过直接调用TinyGPS库,代码更简单,但是光是没有按键选择功能的代码就有将近8KB,而本程序加入按键功能也才8KB大小。对于只需要基本定位信息而言,TinyGPS库可以不用。
GPS板淘宝上找的,如图,最便宜的一个就是了。

请大家指教啊!

fangtaonj 发表于 2013-3-26 11:11:06

太好了,正在找这样的程序!谢谢楼主!

fangtaonj 发表于 2013-3-26 11:16:17

尝试编译,出三处错误:
GPS_pde:173: error: incompatible types in assignment of 'char' to 'char '
GPS_pde.cpp: In function 'byte get_key(unsigned int)':
GPS_pde:182: error: ISO C++ forbids comparison between pointer and integer
我是新手,楼主看看什么问题?
另外楼主请问你接的GPS输出的格式有哪些?我手头的GPS输出格式有五六种之多,还关不掉,感觉处理起来很麻烦。

nngh 发表于 2013-3-26 11:32:46

本帖最后由 nngh 于 2013-3-26 12:31 编辑

fangtaonj 发表于 2013-3-26 11:16 static/image/common/back.gif
尝试编译,出三处错误:
GPS_pde:173: error: incompatible types in assignment of 'char' to 'char ...

握手:handshake,我也是新手上路!我也不太懂。
代码我从IDE窗口直接复制过来的。现在是Arduino1.0.3版本。编译通过,GPS速率9600,GPS板TX3号端口,RX2号端口,程序目前只处理GPMRC信息流。目前只能提供这些了:'(

wing 发表于 2013-3-26 12:03:50

这么给力的作品是绝对要顶的
楼主你的GPS模块多少米啊?

fangtaonj 发表于 2013-3-26 18:26:02

nngh 发表于 2013-3-26 11:32 static/image/common/back.gif
握手,我也是新手上路!我也不太懂。
代码我从IDE窗口直接复制过来的。现在是Arduino1.0.3版 ...

握手!:handshake 我正在准备吧你的程序在我的机器上运行起来。已把你的程序打印下来正在琢磨。请问您的外围电路有哪些?只是在串口接GPS吗?

nngh 发表于 2013-3-26 18:37:56

fangtaonj 发表于 2013-3-26 18:26 static/image/common/back.gif
握手! 我正在准备吧你的程序在我的机器上运行起来。已把你的程序打印下来正在琢磨。请问您的外 ...

对呀,就图片中那么多了,还有个GPS天线没拍到,在窗外。

飞翔的红猪 发表于 2013-3-26 19:05:38

看了半天才看懂,这个GPS解析程序是先接收完一帧,然后才进行解析的~~:L

逆袭de秘银 发表于 2013-3-26 19:24:42

{:soso_e179:}厉害啊

fangtaonj 发表于 2013-3-26 21:14:05

飞翔的红猪 发表于 2013-3-26 19:05 static/image/common/back.gif
看了半天才看懂,这个GPS解析程序是先接收完一帧,然后才进行解析的~~

朋友,我是新手,烦您给解释一下串口读GPS数据那一段程序好吗?我总是有点不太理解。楼主的注释有好些似乎不是英语也不太理解。

fangtaonj 发表于 2013-3-26 21:16:35

nngh 发表于 2013-3-26 18:37 static/image/common/back.gif
对呀,就图片中那么多了,还有个GPS天线没拍到,在窗外。

楼主才看出来你的GPS模块是针对arduino专门设计的模块是嘛?

nngh 发表于 2013-3-28 10:09:56

fangtaonj 发表于 2013-3-26 21:16 static/image/common/back.gif
楼主才看出来你的GPS模块是针对arduino专门设计的模块是嘛?

是,但本程序对所有串行输出的GPS应该都是适用的。本人新手上路,刚开始没搞懂,也为了省事和保险起见,所有板子都在一家店里买了。

nngh 发表于 2013-3-28 10:19:12

fangtaonj 发表于 2013-3-26 21:14 static/image/common/back.gif
朋友,我是新手,烦您给解释一下串口读GPS数据那一段程序好吗?我总是有点不太理解。楼主的注释有好些似乎 ...

简单回复:先接收一段数据,再从中找出$GPRMC数据头,将之后的数据写入一个一维数组中(缓存),读取时以是否检测到逗号来分隔每个数据。
请专业人士补充:)

fangtaonj 发表于 2013-3-28 18:57:13

谢谢楼主!请专业人士在补充!

fangtaonj 发表于 2013-3-28 18:59:48

有高手路过的话请看看那三个错误提示是什么意思?本人纯菜鸟!帮我改好这三个错误我就能跑起来慢慢试验了。谢谢啊!
GPS_pde:173: error: incompatible types in assignment of 'char' to 'char '
GPS_pde.cpp: In function 'byte get_key(unsigned int)':
GPS_pde:182: error: ISO C++ forbids comparison between pointer and integer
页: [1] 2 3
查看完整版本: 简易GPS,本人第一个作品