GPS (Holux M-1000) signal read using Arduino 1: coordinate/bearing/speed and UTC

Holux M-1000 is a GPS receiver with a Bluetooth.  I had used it for navigation with Palm TX and Treo700p (yes, I have long been a big fan of Palm PDA series) and Geocaching until I bought Android smartphone which has a built-in GPS module. So Holux M-1000 has been in my drawer collecting dust for more than a year. From last summer, I began to play golf and I think I’m getting better. 🙂  I searched and found a lot of Android applications for assist golfing with maps, hole/hazard information, showing how many yards left and so on. I’m a gadget mania so I downloaded most of them and tried but none of them satisfied me.  In most of cases, I didn’t bother to pull out my phone and enter my password to unlock the phone and go to the application and run and wait.  Well, one might say that why not keep the phone ON while you are playing? Yes, you can do that. But the GPS module drains battery so fast and I don’t want to miss any phone calls because of the low battery.  Not only because of the battery consumption, I can’t find any application fits me (yet). So I thought I would like to have a GPS module, very simple module, that can show distance between two locations. For example, at a tee, the first location can be marked by pressing a button on the device and from that point, the device shows how far I moved while I walk to the spot for the second shot. And then I can push another (or the same) button to clear the first point and mark current spot as the first location and do the same, and so on. What parts would I need for building this device?  Here is the list of the part I noted.

  • GPS module that can communicate with Arduino (or Jeonlab mini, of course 🙂
  • LCD: I have a 16×2 LCD but it’s bulky and shows only two lines. I have ordered popular Nokia 5110 LCD on ebay and I’m waiting for it as of this writing.
  • Arduino compatible board (preferably small)
  • Power (battery for sure)
  • some resistors, caps, wire, prototyping board, push button switches etc.

That’s when I remembered my old gadget, Holux M-1000.  It has a bluetooth which means it should have serial output somewhere between the GPS module and the bluetooth module.  I started opening up the Holux M-1000 and found RX… TX… on the PCB. YES!!  But I needed to know what voltage requirements for these pins. So I searched on internet for a datasheet or instruction manual of Holux M-1000 and found there were already some people tried to read GPS signal from the Holux M-1000 using either Arduino or PIC microcontrollers. Maybe I was not very lucky to find good articles but none of them were quite useful to give me answers what I wanted clearly.  I found the User’s Manual (1371865.pdf) from Holux homepage and I got very important information as below.

  • Pin 4 and 3 on the USB mini B connector on Holux M-1000 are TX and RX (so I don’t have to add wires from the PCB. That’s good news to make my life easier. In fact, I had thought the USB jack is only for charging the battery!)
  • Pins 5 and 1 of the USB connector are Vcharge (5V) and GND, respectively as standard USB pinouts.
  • Those two RX and TX pins’ voltage range is 3.3 – 5V
  • Data format: NMEA0183 V3.01, GGA, RMC, VTG, GSA, GSV
  • Power consumption: 40 – 50mA in normal mode, 35mA in power saving mode

Now, I’m not familiar with NMEA data format, so I searched on internet again and found some good sites here and there. There are bunch of GPS information you can get from sentences that Holux M-1000 generates periodically as GGA, RMC, VTG, GSA, and GSV, but I don’t need all of them. All I need is latitude and longitude actually, but additional information such as time and date, bearing, speed will be good to know as well.  The sentence, RMC has all of these. Well, program might be simple if I needed to read data from only one sentence, then.  photo Holux-Jeonlab.jpg The GPS (Holux M-1000) and the Jeonlab mini v1.3 with an FTDI breakout board are shown above. I have USB A to mini-B cable but I didn’t want to cut the cable, so I used a female USB-A connector as shown below. Note that only 3 pins (V+, GND, and TX from GPS) are used since I only need to read serial data from the GPS, not sending any command or data to it.  photo USBpinout.jpg And the picture below shows how they connected each other. The FTDI breakout board is connected to my computer through USB cable.  photo Holux-Jeonlabconnection.jpg While I’m testing, I need serial communication between the Jeonlab mini and my computer and I will need these pins (RX, TX on the Jeonlab mini) later when I want to modify my program.  So I decided to use the Softwareserial library which is already included in the Arduino libraries. The TX pin from the GPS is connected to the pin 10 of the Jeonlab mini.  I also connected the V+ from the GPS to the Vcc pin of the Jeonlab mini as well as GND pins. In fact, you don’t need to connect the V+ pins as long as they are powered up by their own power source, but this way I can continuously charge the GPS battery from my computer USB port. I will need more time to finish the whole program while I’m waiting for the LCD that I bought, but let me show how I have programmed so far.  It can read RMC data from the GPS and get the coordinate, bearing, speed, date and time. Thanks to ‘serial parse’ function that is included in the Arduino version >1.0, it was easy to get numeric values from the serial data coming in from the GPS. However, one tricky thing was to get the local time from UTC time and date. I had to consider time zone, DST(daylight saving time), number of days of each month, leap year, etc. That was fun to figure out how to get correct time and date. Here is my code so far. I guess it is quite straight forward, but if you have any question, please add a comment below.

/*
 GPS distance measuring
    - GPS: Holux M-1000
    - Arduino: JeonLab mini v1.3
    - LCD: Nokia 5110
 Programmed by: Jinseok Jeon (JeonLab.wordpress.com)
 Date: Sep 2013
 Revised: Oct 28, 2013
 */

#include <SoftwareSerial.h>
SoftwareSerial gps(10, 0); // RX, TX

const int TimeZone = -5; //EST
int DSTbegin[] = { //DST 2013 - 2025 in Canada and US
  310, 309, 308, 313, 312, 311, 310, 308, 314, 313, 312, 310, 309
};
int DSTend[] = { //DST 2013 - 2025 in Canada and US
  1103, 1102, 1101, 1106, 1105, 1104, 1103, 1101, 1107, 1106, 1105, 1103, 1102
};
int DaysAMonth[] = { //number of days a month
  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int gpsYear;
int gpsMonth;
int gpsDay;
int gpsHour;
byte gpsMin;
byte gpsSec;
//float distance;
float gpsLat0;
float gpsLong0;
float gpsLat;
float gpsLong;
float gpsSpeed; //km/h
float gpsBearing; //deg
boolean LEDstate = false;
boolean SpeedWatch50;
boolean SpeedWatch70;
boolean SpeedWatch120;

#include <LCD5110_Basic.h>

LCD5110 LCD(6, 5, 4, 2, 3); //SCLK, MOSI/DIN, DC, RST, CS/CE
extern uint8_t SmallFont[];      // 6x8 pixels
extern uint8_t MediumNumbers[];  // 12x16 pixels
//extern uint8_t BigNumbers[];     // 14x24 pixels

void setup()
{
  gps.begin(38400);
  pinMode(8, OUTPUT); //LED
  digitalWrite(8, 1); //LED off
  pinMode(7, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  LCD.InitLCD();
  LCD.setContrast(70); //0-127, you need to find proper number
  LCD.setFont(SmallFont);
  LCD.print("JeonLab", RIGHT, 16);
}

void loop()
{
  int a1, a2, b1, b2;
  if (gps.available() > 1)
  {
    if (char(gps.read()) == 'R' && char(gps.read()) == 'M' && char(gps.read()) == 'C')
    {
      gpsTime(gps.parseInt());
      gps.parseFloat(); //discard unnecessary part
      a1 = gps.parseInt();
      a2 = gps.parseInt();
      b1 = gps.parseInt();
      b2 = gps.parseInt();
      gpsLatLong(a1, a2, b1, b2);
      gpsSpeed = gps.parseFloat() * 1.852; //knot to km/h
      gpsBearing = gps.parseFloat();
      gpsDate(gps.parseInt());
      if (gpsYear % 4 == 0) DaysAMonth[1] = 29; //leap year check

      //Time zone adjustment
      gpsHour += TimeZone;
      //DST adjustment
      if (gpsMonth * 100 + gpsDay >= DSTbegin[gpsYear - 13] &&
          gpsMonth * 100 + gpsDay < DSTend[gpsYear - 13]) gpsHour += 1;
      if (gpsHour < 0)
      {
        gpsHour += 24;
        gpsDay -= 1;
        if (gpsDay < 1)
        {
          if (gpsMonth == 1)
          {
            gpsMonth = 12;
            gpsYear -= 1;
          }
          else
          {
            gpsMonth -= 1;
          }
          gpsDay = DaysAMonth[gpsMonth - 1];
        }
      }
      if (gpsHour >= 24)
      {
        gpsHour -= 24;
        gpsDay += 1;
        if (gpsDay > DaysAMonth[gpsMonth - 1])
        {
          gpsDay = 1;
          gpsMonth += 1;
          if (gpsMonth > 12) gpsYear += 1;
        }
      }
      LCD.setFont(MediumNumbers);
      LCD.clrRow(0);//8 pixel high row to clear (0-5)
      LCD.clrRow(1);
      LCD.printNumF(gpsSpeed, 0, LEFT, 0); //km/h
      if (gpsSpeed > 2)
      {
        LCD.printNumF(gpsBearing, 0, RIGHT, 0); //bearing in degree
      }

      LCD.setFont(SmallFont);
      LCD.printNumI(gpsMonth, 0, 24, 2, '0');
      LCD.print("-", 12, 24);
      LCD.printNumI(gpsDay, 18, 24, 2, '0');
      LCD.print("-", 30, 24);
      LCD.printNumI(gpsYear, 36, 24);

      LCD.printNumI(gpsHour, 54, 24, 2, '0');
      LCD.print(":", 66, 24);
      LCD.printNumI(gpsMin, 72, 24, 2, '0');

      if (gpsLat0 != 0.0)
      {
        float distLat = abs(gpsLat0 - gpsLat) * 111194.9;
        float distLong = 111194.9 * abs(gpsLong0 - gpsLong) * cos(radians((gpsLat0 + gpsLat) / 2));
        float distance = sqrt(pow(distLat, 2) + pow(distLong, 2));

        LCD.clrRow(4);//8 pixel high row to clear (0-5)
        LCD.clrRow(5);
        LCD.printNumF(distance, 0, LEFT, 32);
        LCD.print("meter", RIGHT, 32);
        LCD.printNumF(distance / 0.9144, 0, LEFT, 40);
        LCD.print("yard", RIGHT, 40);
      }
      if (gpsSpeed <= 50) SpeedWatch50 = 0;
      if (gpsSpeed <= 70) SpeedWatch70 = 0;
      if (gpsSpeed <= 120) SpeedWatch120 = 0;

      if (gpsSpeed > 50 && SpeedWatch50 == 0)
      {
        SpeedWatch50 = 1;
        tone(11, 4978, 100);
        delay(100);
      }
      if (gpsSpeed > 70 && SpeedWatch70 == 0)
      {
        SpeedWatch70 = 1;
        for (int i = 1; i <= 2; i++)
        {
          tone(11, 4978, 100);
          delay(500);
        }
      }
      if (gpsSpeed > 120 && SpeedWatch120 == 0)
      {
        SpeedWatch120 = 1;
        for (int i = 1; i <= 3; i++)
        {
          tone(11, 4978, 100);
          delay(500);
        }
      }
    }
  }
  if (digitalRead(12) == LOW) //marking current position
  {
    tone(11, 3140, 100);
    if (gpsLat0 == 0.0)
    {
      gpsLat0 = gpsLat;
      gpsLong0 = gpsLong;
    }
    else
    {
      gpsLat0 = 0.0;
      gpsLong0 = 0.0;
      LCD.clrRow(4);
      LCD.clrRow(5);
    }
    delay(700);
  }
  if (digitalRead(7) == LOW) //LED backlight toggle
  {
    tone(11, 2810, 100);
    digitalWrite(8, LEDstate); //LED on
    LEDstate = !LEDstate;
    delay(700);
  }
  if (digitalRead(9) == LOW) //for future functional button
  {
    tone(11, 3910, 100);

  }
}

void gpsTime(long UTC)
{
  gpsHour = int(UTC / 10000);
  gpsMin = int(UTC % 10000 / 100);
  gpsSec = UTC % 100;
}

void gpsLatLong(int lat1, int lat2, int long1, int long2)
{
  gpsLat = int(lat1 / 100) + (lat1 % 100) / 60.0 + float(lat2) / 10000.0 / 60.0;
  gpsLong = int(long1 / 100) + (long1 % 100) / 60.0 + float(long2) / 10000.0 / 60.0;
}

void gpsDate(long dateRead)
{
  gpsDay = int(dateRead / 10000);
  gpsMonth = int(dateRead % 10000 / 100);
  gpsYear = dateRead % 100; //last 2 digits, e.g. 2013-> 13
}

13 thoughts on “GPS (Holux M-1000) signal read using Arduino 1: coordinate/bearing/speed and UTC

    • As mentioned already, this code is for Holux M-1000. If you have other GPS, you need to review GPS data first. If you don’t know how to interprete GPS data of your own GPS, probably other library will be better option for you.

  1. Nice to have your post shared! Thanks!! I am going the same path! Used Palm, Dell PDA with Holux M-1000, and now starting with Arduino! Hope to gather all the parts and get to the doing and see from there! Making this blog my favorite!! Again, thanks for sharing it! Robert Calgary, AB

    • Robert: I’m glad to know that there is someone who has similar interests with me. Let me know your progress and if you have any questions, send me an email. I’ll be happy to help you. Good luck!

  2. Neon lab! 🙂 it’s been long holidays and long waiting fir my parts to arrive from China!
    I am no, about 80% of my parts and more to come…. It’s been a long and slow process!!
    Yes! Definitely I’ll have questions fir you, since I have VERY rusty program skill plus electronic is something I JUST pick up since last fall! Again, very thankful for your posts! You know, sharing knowledge maks our humanity a better place! Will update once I start…!!
    Robert

  3. This is awesome. Here I am looking at spending ~40$ on a GPS for my arduino when I pull out my M-1000 from god knows how long ago i got it and say hell let me good it see if anyone else has tried this. Brilliant, thanks for doing the leg work!

Leave a reply to jeonlab Cancel reply