Skip to content

Instantly share code, notes, and snippets.

@SlightlyLoony
Created October 16, 2017 18:03
Show Gist options
  • Save SlightlyLoony/9493869d31b76cb4d81aba302d00f782 to your computer and use it in GitHub Desktop.
Save SlightlyLoony/9493869d31b76cb4d81aba302d00f782 to your computer and use it in GitHub Desktop.
Utility for working with UBlox GPS on Raspberry Pi 3 running Jessie or later
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <argp.h>
/*
Simple program to read configuration of U-Blox GPS serial port.
*/
///// begin ARGP stuff
const char *argp_program_version = "gpscfg 0.1";
const char *argp_program_bug_address = "<[email protected]>";
static char doc[] = "gpscfg - configure U-Blox GPS on Raspberry Pi 3 port serial0";
/* A description of the arguments we accept. */
static char args_doc[] = "";
/* The options we understand. */
static struct argp_option options[] = {
{ "baud", 'b', 0, 0, "Check baud rate of GPS" },
{ "read", 'r', 0, 0, "Read port configuration" },
{ "echo", 'e', 0, 0, "Echo GPS output to console" },
{ "quiet", 'q', 0, 0, "Quiet mode (fewer messages)" },
{ "toggle", 't', 0, 0, "Toggle baud rate between 9,600 and 115,200" },
{ "high", 'h', 0, 0, "Try synchronizing at 115,200 baud first" },
{ 0 }
};
/* Used by main to communicate with parse_opt. */
struct arguments {
int baudCheck;
int readPortCfg;
int toggle;
int echo;
int quiet;
int high;
};
/* Parse a single option. */
static error_t parse_opt (int key, char *arg, struct argp_state *state) {
/* Get the input argument from argp_parse, which we
know is a pointer to our arguments structure. */
struct arguments *arguments = state->input;
switch (key) {
case 'r': arguments->readPortCfg = 1; break;
case 'b': arguments->baudCheck = 1; break;
case 'q': arguments->quiet = 1; break;
case 't': arguments->toggle = 1; break;
case 'e': arguments->echo = 1; break;
case 'h': arguments->high = 1; break;
case ARGP_KEY_END:
if( arguments->baudCheck + arguments->readPortCfg + arguments->echo
+ arguments->toggle == 0 )
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
/* Our argp parser. */
static struct argp argp = { options, parse_opt, args_doc, doc };
///// end ARGP stuff
typedef struct ubxMsg {
int size;
unsigned char* msg;
int noMsg;
int valid;
int class;
int id;
int bodySize;
unsigned char* body;
} UBXMsg;
unsigned char portQueryBody[] = {
0xB5, 0x62, /* message header */ \
0x06, 0x00, /* message class and ID */ \
0x01, 0x00, /* message body length */ \
0x01, /* UART ID */ \
0x00, 0x00 /* checksum */ \
};
unsigned char save[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1D, 0xAB};
UBXMsg createUbxMsg( unsigned char class, unsigned char id, unsigned char* body, int bodySize ) {
UBXMsg result;
result.msg = calloc( bodySize + 8, 1 );
result.msg[0] = 0xB5;
result.msg[1] = 0x62;
result.msg[2] = class;
result.msg[3] = id;
result.msg[4] = bodySize & 0xFF;
result.msg[5] = bodySize >> 8;
memcpy( result.msg + 6, body, bodySize );
// calculate Fletcher checksum for this message...
unsigned char ck_a = 0;
unsigned char ck_b = 0;
int i;
for( i = 2; i < bodySize + 6; i++ ) {
ck_a += result.msg[i];
ck_b += ck_a;
}
result.msg[bodySize + 6] = ck_a;
result.msg[bodySize + 7] = ck_b;
result.size = bodySize + 8;
result.bodySize = bodySize;
result.id = id;
result.class = class;
result.valid = 1;
result.noMsg = 0;
result.body = body;
return result;
}
UBXMsg getPortQueryMsg() {
unsigned char body[] = { 0x01 };
return createUbxMsg( 0x06, 0x00, body, 1 );
}
// returns character if available, otherwise -1
int readUART( int fd ) {
// Read a character from the port, if there is one
unsigned char rx_buffer[1];
int rx_length = read( fd, (void*)rx_buffer, 1 ); //Filestream, buffer to store in, number of bytes to read (max)
if (rx_length < 0)
{
if( rx_length < -1 ) {
printf( "Read error, %d\n", rx_length );
return -2;
}
return -1;
}
else if (rx_length == 0)
{
//No data waiting
return -1;
}
else
{
//Bytes received
return rx_buffer[0];
}
}
// echoes characters received from GPS onto console; never exits
void echo( int fd ) {
while( 1 ) {
usleep( 50 );
int c = readUART( fd );
if( c >= 0 ) printf( "%c", c );
}
}
// waits up to five seconds for the given string to be read
// returns 0 for failure, 1 for success
int waitForString( int fd, char* str, int size ) {
int attempts = 0;
int gotIt = 0;
int index = 0;
while( (attempts < 100000) && (gotIt == 0) ) {
usleep( 50 );
int x = readUART( fd );
if( x >= 0 ) {
//printf("%c", x);
if( x == str[index] ) {
index++;
if( index == size ) {
gotIt = 1;
}
}
else {
index = 0;
}
}
attempts++;
}
return gotIt;
}
// Waits up to five seconds for a known message ("$GNGGA") to appear,
// returning 1 if synchronized, 0 otherwise.
// this is needed because upon opening the port the UART's receiver may
// not be synchronized to the data stream, and will return garbage data.
// Once synchronized, it should stay synchronized.
int syncRx( int fd ) {
return (waitForString( fd, "$GNGGA,", 7 ) == 0 ) ? 0 : 1;
}
void setTermOptions( int fd, int baud ) {
struct termios options;
tcgetattr( fd, &options );
options.c_cflag = baud | CS8 | CLOCAL | CREAD;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush( fd, TCIFLUSH );
tcsetattr( fd, TCSANOW, &options );
}
// returns file descriptor or -1 if failure...
// hint is 1 to try 115,200 baud first
int openUART( int quiet, int hint ) {
int fd = -1;
fd = open("/dev/serial0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (fd == -1) {
printf( "Can't open serial0 - possibly it's in use by another application\n" );
return fd;
}
// we try twice...
int try = (hint ? B115200 : B9600);
int i;
for( i = 0; i < 2; i++ ) {
setTermOptions( fd, try );
if( syncRx( fd ) ) {
char* baud = ((try == B9600) ? "9,600" : "115,200" );
if( !quiet ) printf( "Synchronized at %s baud...\n", baud );
return fd;
}
try = ((try == B9600) ? B115200 : B9600);
}
// we failed, so close the port...
close( fd );
return -1;
}
// Waits up to five seconds for a UBX message to be received.
// Entire message is received, validated, and decoded.
// If no message is received, the noMsg flag will be set.
// If a valid message is received, the valid flag will be set.
UBXMsg rcvUbxMsg( fd ) {
struct ubxMsg result;
result.noMsg = 1;
result.valid = 0;
int attempts = 0;
int gotIt = 0;
int index = 0;
unsigned char header[6];
// first we read the six byte header starting with 0xB5 0x62...
while( (attempts < 100000) && (gotIt == 0) ) {
usleep( 50 );
int x = readUART( fd );
if( x >= 0 ) {
header[index] = x;
switch( index ) {
case 0: if( x == 0xB5 ) index++; break;
case 1: if( x == 0x62 ) index++; else index = 0; break;
default: index++; if( index >= 6 ) gotIt = 1; break;
}
}
attempts++;
}
if( !gotIt )
return result; // noMsg flag is set by default...
// decode the header...
result.bodySize = header[4] | (header[5] << 8);
result.class = header[2];
result.id = header[3];
// then we read the body, however long it is, plus the two byte checksum...
gotIt = 0;
index = 0;
result.body = calloc( result.bodySize + 2, 1 );
while( (attempts < 100000) && (gotIt == 0) ) {
usleep( 50 );
int x = readUART( fd );
if( x >= 0 ) {
result.body[index++] = x;
if( index >= result.bodySize + 2 )
gotIt = 1;
}
attempts++;
}
if( !gotIt )
return result; // noMsg flag is set by default...
// we got the message, but we're not sure it's valid yet...
result.noMsg = 0;
// construct the raw message buffer...
result.msg = calloc( result.bodySize + 8, 1 );
memcpy( result.msg, header, 6 );
memcpy( result.msg + 6, result.body, result.bodySize + 2 );
// now we check to see if the message is valid (checksum matches)...
// calculate Fletcher checksum for this message...
unsigned char ck_a = 0;
unsigned char ck_b = 0;
int i;
for( i = 2; i < result.bodySize + 6; i++ ) {
ck_a += result.msg[i];
ck_b += ck_a;
}
if( (result.msg[result.bodySize + 6] == ck_a) && (result.msg[result.bodySize + 7] == ck_b) ) {
result.valid = 1;
}
return result;
}
// return 0 if error, 1 if succeeded
int sendUbxMsg( int fd, UBXMsg msg ) {
// first we blast the message out...
//printf("message size: %d\n", msg.size);
int c = write( fd, msg.msg, msg.size );
free( msg.msg );
return 1;
}
// prints the body of the given UBX message in hex, or the raw message if the body is invalid
void printUbxMsg( UBXMsg msg ) {
int size = msg.body ? msg.bodySize : msg.size;
unsigned char * buf = msg.body ? msg.body : msg.msg;
printf( msg.body ? "body:" : "msg:" );
int i;
for( i = 0; i < size; i++ ) {
printf( "%02x:", buf[i] );
}
printf( "\n" );
}
// interprets the given port configuration message and prints the result
void printUbxPortConfiguration( UBXMsg msg ) {
if( msg.noMsg || !msg.valid ) {
printf( "Missing or invalid port configuration message!\n" );
return;
}
// get the baud rate...
int baud = msg.body[8] | (msg.body[9] << 8) | (msg.body[10] << 16) | (msg.body[11] << 24);
printf( "%d baud\n", baud );
// get the mode...
int mode = msg.body[4] | (msg.body[5] << 8) | (msg.body[6] << 16) | (msg.body[7] << 24);
int bits = 0x03 & (mode >> 6);
int parity = 0x07 & (mode >> 9);
int stop = 0x03 & (mode >> 12);
printf( "%d bits\n", bits + 5 );
switch( parity ) {
case 0: printf( "even parity\n" ); break;
case 1: printf( "odd parity\n" ); break;
case 4: case 5: printf( "no parity\n" ); break;
default: printf( "invalid parity\n" ); break;
}
switch( stop ) {
case 0: printf( "1 stop bit\n" ); break;
case 1: printf( "1.5 stop bits\n" ); break;
case 2: printf( "2 stop bits\n" ); break;
case 3: printf( "0.5 stop bits\n" ); break;
}
}
// toggles the UART's baud rate between 9,600 baud and 115,200 baud
void toggle( int fd, int quiet ) {
// first we get the current GPS port configuration
int result = sendUbxMsg( fd, getPortQueryMsg() );
if( !result ) {
printf( "Failed to send port query message!\n" );
exit( 1 );
}
UBXMsg cc = rcvUbxMsg( fd );
if( cc.noMsg || !cc.valid ) {
printf( "Port query response missing or invalid!\n" );
exit( 1 );
}
// now we construct our new port configuration message...
int curBaud = cc.body[8] | (cc.body[9] << 8) | (cc.body[10] << 16) | (cc.body[11] << 24);
int newBaud = (curBaud == 9600) ? 115200: 9600;
cc.body[8] = newBaud;
cc.body[9] = newBaud >> 8;
cc.body[10] = newBaud >> 16;
cc.body[11] = newBaud >> 24;
UBXMsg setCfg = createUbxMsg( 0x06, 0x00, cc.body, 20 );
// wait until we've had no rx data for at least three characters
// we're hoping to hit a pause between the GPS sending bursts of characters
int wt = (curBaud == 9600) ? 2813 : 234;
int ct = 0;
while( ct < wt ) {
usleep( 10 );
int c = readUART( fd );
ct = (( c >= 0 ) ? 0 : ct + 10);
}
// send the configuration message...
result = sendUbxMsg( fd, setCfg );
if( !result ) {
printf( "Failed to send port configuration message!\n" );
return;
}
tcdrain( fd ); // wait for the preceding message to finish sending...
// we ignore the expected ACK, because we don't know what baud rate it's going
// to come back in
if( !quiet ) printf( "Port configuration changed to %d baud\n", newBaud );
}
int main( int argc, char *argv[] ) {
//// begin ARGP stuff
struct arguments arguments;
/* Default values. */
arguments.baudCheck = 0;
arguments.readPortCfg = 0;
arguments.echo = 0;
arguments.quiet = 0;
arguments.toggle = 0;
arguments.high = 0;
/* Parse our arguments; every option seen by parse_opt will
be reflected in arguments. */
argp_parse (&argp, argc, argv, 0, 0, &arguments);
//// end ARGP stuff
int fd = openUART( arguments.quiet, arguments.high );
if( fd < 0 ) exit( 1 );
if( arguments.baudCheck ) {
close( fd );
exit( 0 );
}
else if( arguments.toggle ) {
toggle( fd, arguments.quiet );
}
else if( arguments.readPortCfg ) {
int result = sendUbxMsg( fd, getPortQueryMsg() );
if( result ) {
UBXMsg answer = rcvUbxMsg( fd );
printUbxPortConfiguration( answer );
}
}
else if( arguments.echo ) {
echo( fd );
}
exit( 0 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment