Sunday, February 23, 2014

SoftwareSerial and GPS



How to use SofwareSerial library to connect a Serial GPS on the Official Arduino Robot? 

We have chosen for our robot this excellent "Ultimate GPS" module from Adafruit, because it requires only 2 pins (using serial communication) to communicate with and it's plenty of good functionality (autonomous GPS logger, Time RTC, energy safer...).


Wiring

Due to lack of memory on the Control Board, we've decided to connect the GPS to the Motor board.
2 digital pin included one using interrupts are required to connect the GPS to the Motor Board.
We decide to connect the TX pin of the GPS to the SPI/ICSP connector on MOSI pin because it's a Digital pin with interrupt management and connector was not used on this board.


The RX pin of the GPS must be connected to another digital pin, we choose TK3 (B_TK3 on the picture)

Coding

You have to include the SoftwareSerial library to your project but with Arduino IDE 1.5.4 some core arduino header files must be patched to be able to compile.
You have to add these 4 definition (digitalPinToPCICR, digitalPinToPCICRbit, digitalPinToPCMSK, digitalPinToPCMSKbit(p) ) into the pin_arduino.h file in these following directories:

  • ...\Arduino\hardware\arduino\avr\variants\robot_control 
  • ...\Arduino\hardware\arduino\avr\variants\robot_motor
#define digitalPinToPCICR(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCICR) : ((uint8_t *)0))
#define digitalPinToPCICRbit(p) 0
#define digitalPinToPCMSK(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCMSK0) : ((uint8_t *)0))
#define digitalPinToPCMSKbit(p) ( ((p) >= 8 && (p) <= 11) ? (p) - 4 : ((p) == 14 ? 3 : ((p) == 15 ? 1 : ((p) == 16 ? 2 : ((p) == 17 ? 0 : (p - A8 + 4))))))
// __AVR_ATmega32U4__ has an unusual mapping of pins to channels
extern const uint8_t PROGMEM analog_pin_to_channel_PGM[];
#define analogPinToChannel(P) ( pgm_read_byte( analog_pin_to_channel_PGM + (P) ) )
view raw pins_arduino.h hosted with ❤ by GitHub


Command must be created on both board to communicate and share GPS request/answers.

Command on the Control Board
// Used to convert Bytes to float
union float2bytes { float f; char b[sizeof(float)]; };
// Read GPS coordinate from the Motor Board
void RobotControl::readGPSCoord(GPS_DATA &gpsData){
messageOut.writeByte(COMMAND_READ_GPS_COORD);
messageOut.sendData();
delay(10);
while(!messageIn.receiveData());
uint8_t mess = messageIn.readByte();
if(mess==COMMAND_READ_GPS_COORD_RE){
gpsData.fix = messageIn.readByte();
gpsData.sat = messageIn.readByte();
float2bytes f2b;
uint8_t i;
for ( i=0; i < 4; i++ )
f2b.b[i] = messageIn.readByte();
gpsData.lat = f2b.f;
for ( i=0; i < 4; i++ )
f2b.b[i] = messageIn.readByte();
gpsData.lon = f2b.f;
for ( i=0; i < 4; i++ )
f2b.b[i] = messageIn.readByte();
gpsData.alt = f2b.f;
}
}
view raw awbbSensors.cpp hosted with ❤ by GitHub


Response on the Motor Board
void RobotMotorBoard::parseCommand(){
uint8_t modeName;
uint8_t codename;
//...
if(this->messageIn.receiveData()){
//Serial.println("data received");
uint8_t command=messageIn.readByte();
//Serial.println(command);
switch(command){
//...
case COMMAND_READ_GPS_COORD:
_readGPSCoord();
break;
//...
}
}
}
// Used to convert Bytes to float
union float2bytes { float f; char b[sizeof(float)]; };
// Send GPS coordinate to the Control Board
void RobotMotorBoard::_readGPSCoord(){
messageOut.writeByte(COMMAND_READ_GPS_COORD_RE);
messageOut.writeByte(GPS.fix);
messageOut.writeByte(GPS.satellites);
float2bytes f2b;
uint8_t i;
f2b.f = GPS.latitude;
for ( i=0; i < sizeof(float); i++ )
messageOut.writeByte(f2b.b[i]);
f2b.f = GPS.longitude;
for ( i=0; i < sizeof(float); i++ )
messageOut.writeByte(f2b.b[i]);
f2b.f = GPS.altitude;
for ( i=0; i < sizeof(float); i++ )
messageOut.writeByte(f2b.b[i]);
messageOut.sendData();
}

GPS Management has been coded without interrupt due to problem using Interrupt, SoftwareSerial library and Serial.print() function (I still not not have answer for this trouble, any idea?).

How to well understand the GPS information received?

According to the GPS, a location as 4042.6142,N (Latitude 40 degrees, 42.6142 decimal minutes North) & 07400.4168,W. (Longitude 74 degrees, 0.4168 decimal minutes West) To look at this location in Google maps, type +40° 42.6142', -74° 00.4168' into the google maps search box . Unfortunately gmaps requires you to use +/- instead of NSWE notation. N and E are positive, S and W are negative. The code has been adapted in consequence. 

People often get confused because the GPS is working but is "5 miles off" - this is because they are not parsing the lat/long data correctly. Despite appearances, the geolocation data is NOT in decimal degrees. It is in degrees and minutes in the following format: Latitude: DDMM.MMMM (The first two characters are the degrees.) Longitude: DDDMM.MMMM (The first three characters are the degrees.)    

Related link:


No comments:

Post a Comment