Saturday, November 23, 2013

C Code to Read GPS Data via Serial on the Beaglebone Black

I've posted before abut using the Copernicus II GPS module from Sparkfun with a Python script.  The following example is a C program that reads serial data from this module.  The focus here is really on accessing the serial port on the Beaglebone Black, sending a configuration command to the GPS module, and continuously reading the output.  Data is just printed to the Linux command line on the Beaglebone Black. This example does not attempt to format or use this data.


GPS Module  Beaglebone Black
VCC         P9 3
GND         P9 1
TX-B        P9 26
RX-B        P9 24

In order to use the serial port, you need to configure the pins that connect to it.  See my post, "uart1.dts: Configuration File to Enable UART1 on Beaglebone Black," for the configuration file.  The C code below will load this pin configuration file.

C Code

This code is based on sample serial port code by Gary Frerking that I found at the Linux Documentation Project.

For more information about the commands to configure the Copernicus II, see Appendix C of the Copernicus II manual.  As was the case with the Python example, each command must include a 2-digit checksum after the asterisk.  See this page for an online tool to calculate the checksum.  

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>

#include <stdlib.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
   included by <termios.h> */
#define BAUDRATE B115200   // Change as needed, keep B

/* change this definition for the correct port */
#define MODEMDEVICE "/dev/ttyO1" //Beaglebone Black serial port

#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0
#define TRUE 1

    int fd, c, res;
    struct termios oldtio, newtio;
    char buf[255];
    // Load the pin configuration
    int ret = system("echo uart1 > /sys/devices/bone_capemgr.9/slots");
    /* Open modem device for reading and writing and not as controlling tty
       because we don't want to get killed if linenoise sends CTRL-C. */
    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
    if (fd < 0) { perror(MODEMDEVICE); exit(-1); }

    bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

    /* BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
       CRTSCTS : output hardware flow control (only used if the cable has
                 all necessary lines. See sect. 7 of Serial-HOWTO)
       CS8     : 8n1 (8bit,no parity,1 stopbit)
       CLOCAL  : local connection, no modem contol
       CREAD   : enable receiving characters */
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

    /* IGNPAR  : ignore bytes with parity errors
       otherwise make device raw (no other input processing) */
    newtio.c_iflag = IGNPAR;

    /*  Raw output  */
    newtio.c_oflag = 0;

    /* ICANON  : enable canonical input
       disable all echo functionality, and don't send signals to calling program */
    newtio.c_lflag = ICANON;
    /* now clean the modem line and activate the settings for the port */
    tcflush(fd, TCIFLUSH);
    // NMEA command to ouput all sentences
    // Note that this code & format values in manual are hexadecimal
    write(fd, "$PTNLSNM,273F,01*27\r\n", 21);
    /* terminal settings done, now handle input*/
    while (TRUE) {     /* loop continuously */
        /*  read blocks program execution until a line terminating character is
            input, even if more than 255 chars are input. If the number
            of characters read is smaller than the number of chars available,
            subsequent reads will return the remaining chars. res will be set
            to the actual number of characters actually read */
        res = read(fd, buf, 255);
        buf[res] = 0;             /* set end of string, so we can printf */
        printf("%s", buf, res);
    tcsetattr(fd, TCSANOW, &oldtio);

Compiling & Running the Code

Compiling the code at the command line on the Beaglebone Black is very easy.  Change the commands to match the file name in which you've saved the source code -

gcc serial_gps.c -o serial_gps

And the following will run the new program until the user presses control-c -