Saturday, 18 January 2014

GPS version 2

In the last post I showed my first attempt at using a GPS module and displaying the position, time and date.

I have since updated this to use a larger screen. Here are some pictures

2014 01 18 15 17 39

This is the GPS module sitting on a breadboard shield on top of an Arduino Uno.

2014 01 18 10 00 51

This is the display on the LCD.

To code the display I have used a library called u8glib.h available on GitHub. This is a quite complex library. It allows you to create an LCD object and defines a number of methods for acting on it.

There are a number of ways to define a "construct" (or object) but I have used this simple one for the display which is 128 x 64 with an ST7920 controller.

U8GLIB_ST7920_128X64_1X lcd(SCK, MOSI, SS);


The Arduino pins SCK is the SPI bus clock, MOSI is the data line and SS is the chip select. These are connected to the display LCD signals E, R/W and RS.

Methods

lcd.begin(); // reset display and put in default state
lcd.enableCursor(); // cursor visible
lcd,disableCursor(); // cursor not visible (default state)
lcd.setCursorPos(x, y); // cursor to x, y
lcd.setColorindex(col); // BW on = 1, off = 0
lcd.drawBitMap(x, y, cnt, h, *bitmap); // draw bit map of cnt bytes
                                       // width is cnt * 8
                                       // at x, y (top left) of height h
lcd.drawBox(x, y, w, h); // filled box at x, y. width w, height h
                         // fill set by color index
lcd.drawCircle(x, y, rad, opt); // x, y centre of circle radius rad, 
                                // opt is option to draw part of circle
                                // U8G_DRAW_UPPER_RIGHT, ...LEFT,
                                //  ... LOWER_LEFT, ... RIGHT, ...ALL
lcd.drawDisk(x, y, rad, opt); // as above but filled circle
lcd.drawFrame(x, y, w, h); // frame at x, y width w height h, color index
lcd.drawRFrame(x, y, w, h, r); // rounded frame, edges radius r
lcd.drawHLine(x, y, w); // draw horizontal line from x,y width w (pixels)
lcd.drawVLine(x, y, h); // draw vertical line from x, y height h (pixels)
lcd.drawLine(x1, y1, x2, y2,); // draw line from x1, y1 to x2, y2
drawTriangle(x1, y1, x2, y2, x3, y3); // filled triangle
lcd.drawPixel(x, y); // pixel at x, y
lcd.setFont(*font); // sets font - see table below
lcd.getStrWidth(*s); // returns string width in pixels in current font
lcd.drawStr(x, y, *s); // string at x, lower left of characters in setFont
                       // variations on this draw at 90, 180, 270 degrees
lcd.firstPage(); // starts a “picture loop”
lcd.nextPage(); // marks end of “picture loop”, returns 0 if finished


The display can use a wide range of fonts, the one I used is called "u8g_font_helvR08" or Helvetica Regular 8 point.

Code

#include "SoftwareSerial.h"
#include "U8glib.h"

// GPS comms 11 RX, 10 TX and LCD u8g object 6 SCK(E), 5 MOSI(RW), 4 SS(RS)
SoftwareSerial gps(10, 11);
U8GLIB_ST7920_128X64_1X u8g(6, 5, 4);

void setup()
{
    gps.begin(9600); // start GPS serial
}

void loop()
{
  char gpsbuf[200];
  char ebuf[9][20];
  
  readgps(gpsbuf); // read each $GPxxx NMEAstrings+CRLF into gpsbuf

    
  // use $GPRMC, 142759.00,A,5204.81158,N,00120.76633,W,0.107,,,090114,,,A*67
  // fields 1, 3, 4, 5, 6, 9
  if(strncmp(gpsbuf, "$GPRMC", 6) == 0)
  { 
    extract(1, gpsbuf, ebuf[1]); // get utc
    fmt(ebuf[1], "**:**:**"); // format utc
   
    extract(3, gpsbuf, ebuf[3]); // get lat
    fmt(ebuf[3], "** ****"); // format lat

    extract(4, gpsbuf, ebuf[4]); // get N|S
    fmt(ebuf[4], "*"); // format N|S

    extract(5, gpsbuf, ebuf[5]); // get lon
    fmt(ebuf[5], "*** ****"); // format lon

    extract(6, gpsbuf, ebuf[6]); // get E|W
    fmt(ebuf[6], "*"); // format E|W

    extract(9, gpsbuf, ebuf[9]); // get date
    fmt(ebuf[9], "**-**-**"); // format date
    
    u8g.firstPage();
    do
    {
      u8g.setFont(u8g_font_helvR08);
      
      u8g.drawStr(2, 8, "Latitude");            
      u8g.drawStr(66, 8, "Longitude");
      
      u8g.drawStr(2, 19, ebuf[3]); // Lat
      u8g.drawStr(40, 19, ebuf[4]);
      u8g.drawStr(66, 19, ebuf[5]); // Lon
      u8g.drawStr(110, 19, ebuf[6]);
      
      u8g.drawStr(2, 40, "Time");
      u8g.drawStr(66, 40, "Date");
      
      u8g.drawStr(2, 51, ebuf[1]); // UTC
      u8g.drawStr(66, 51, ebuf[9]); // Date   
    } while(u8g.nextPage());    
  }
}

// read GPxxx NMEA string into buffer
void readgps(char *buffer) 
{
  int bp;
  char c;
  
  bp = 0;
  do
  {
    c = gps.read();
    if(c == -1) continue; // gps sends -1 when idle
    buffer[bp++] = c;
  } while(c != '\n'); // ends with LF
  buffer[bp] = '\0'; // add '\0'
}

// extract string from field 0-10 from inbuf to outbuf
void extract(int field, char *inbuf, char *outbuf)
{
  int fcount; // field counter
  int ip; // inbuf pointer
  int op; // outbuf ptr

  fcount = 0;
  ip = 0;
  op = 0;
  
  while(fcount != field) // find field
  {
    while(inbuf[ip++] != ',');
    fcount++;
  }
  
  while(inbuf[ip] != ',') // copy inbuf to outbuf
  {
    outbuf[op++] = inbuf[ip++];
  }
  outbuf[op] = '\0'; // mark end of string
}

// formats buffer with fmtstr, result back in buffer
void fmt(char *buffer, char *fmtstr)
{
  char tmp[20];
  int bp, fp, tp; // buffer, fmtstr & tmp pointers
  
  bp = 0;
  fp = 0;
  tp = 0;
  
  strcpy(tmp, buffer);
  while(fmtstr[fp] != '\0')
  {
    if(fmtstr[fp] == '*')
    {
      buffer[bp] = tmp[tp++];
    }
    else 
    {
      buffer[bp] = fmtstr[fp];
    }
    fp++;
    bp++;
  }
  buffer[bp] = '\0';
}

No comments: