Importing a bunch of pages from old websites.
[website_subgeniuskitty.com] / data / development / misc / garmin_edge_305_linux.md

Linux Support

Overview

Linux support for Garmin GPS devices, especially those which monitor non-location data such as heartrate, is very poor. The situation is further confused by a plethora of conflicting, poorly documented file formats and outdated instructions. The following covers my process for downloading information from my Garmin Edge 305 bicycling GPS to a Linux workstation, including automatic file conversion to the TCX format since it is both supported by GoldenCheetah cycling software and, as an easily manipulated XML based format, should be easily convertable into other formats for many years to come.

Instructions

First, disable the garmin_gps kernel module. On Debian wheezy this is accomplished by writing the following line to /etc/modprobe.d/garmin-gps-blacklist.conf

blacklist garmin_gps

Download, build and install the garmintools package. The tarball contains a README that explains this process. This provides the garmin_save_runs program which will download data from your Garmin GPS and save it in the Garmin binary GMN format. It also includes the garmin_dump program which will convert a GMN file to an XML-life format. Note that you will need to be root, or set appropriate permissions for your user to have USB access. A sample run is shown below.

Extracting data from Garmin EDGE305 Software Version 3.20
Files will be saved in '/mnt/documents/Bicycling/logged_data'
Wrote:   /mnt/documents/Bicycling/logged_data/2014/07/20140724T230419.gmn
Wrote:   /mnt/documents/Bicycling/logged_data/2014/07/20140729T152915.gmn

Once you have obtained a GMN file, download the garmin-dev-master zip file. The gmn2tcx program accepts the GMN file as its sole argument and outputs the TCX file to stdout, as well as status messages (including success messages) to stderr. You will need to have garmin_dump from the garmintools package in your PATH environment variable. As an example, the following will create a new TCX file from an existing GMN file while still displaying status messages on the terminal.

ataylor:~$ /usr/bin/gmn2tcx ./sample.gmn > ./sample.tcx
- validates

To simplify the process, I created a small wrapper script that wraps these tools and performs the following operations.

The script uses python and can be configured by editing the configuration values located in the first few lines. After each cycling session you will only need to connect your Garmin device, execute the script and then launch the cycling software of your choice.

Wrapper Script

#!/usr/bin/python

# Wrapper script to dump files from Garmin Edge 305 and convert to TCX for import into GoldenCheetah
# Requires garmintools and garmin-dev-master packages
# Needs root permissions for USB access

# Aaron Taylor
# ataylor@subgeniuskitty.com

# Configuration
# path to garmin_save_runs, garmin_dump and libgarmintools.so
path_garmintools = "/home/ataylor/bin/garmintools"
# path to gmn2tcx and support files (saxon, etc)
path_gmn2tcx = "/home/ataylor/bin/gmn2tcx"
# path to save gmn data files
path_save_gmn = "/mnt/documents/Bicycling/logged_data"
# path to save tcx files
path_save_tcx = "/mnt/documents/Bicycling/logged_data/temp_tcx"
# path to log file for garmintools output
path_log = "/tmp/garmintools.log"
# UID/GID to set on new files
uid = 1000
gid = 1000

import subprocess, os

# Set up the environment. We want to append to PATH and LD_LIBRARY_PATH
# in case some system files are in a funky place. However, we must first
# check that the variables exist since they might be blank (like 
# LD_LIBRARY_PATH on my system).
my_env = os.environ
try:
    my_env["PATH"]
except:
    my_env["PATH"] = path_garmintools + ":" + path_gmn2tcx
else: 
    my_env["PATH"] = my_env["PATH"] + ":" + path_garmintools + ":" + path_gmn2tcx
try:
    my_env["LD_LIBRARY_PATH"]
except: 
    my_env["LD_LIBRARY_PATH"] = path_garmintools
else:
    my_env["LD_LIBRARY_PATH"] = my_env["LD_LIBRARY_PATH"] + ":" + path_garmintools
my_env["GARMIN_SAVE_RUNS"] = path_save_gmn

# Export the data from the GPS as binary gmn files and store STDOUT in result.
result = subprocess.check_output(['/home/ataylor/bin/garmintools/garmin_save_runs'])

# Newly written files will have a line starting with "Wrote:   " and then the full 
# path to the new file. We want to extract only these lines and add them to the 
# written_files list.
line_start = 0
written_files = []
n = 0
for i in result:
    if i == b'\n':
        line_start = n+1
    elif line_start == n:
        first_chars = "" 
        for k in range(9):
            first_chars += result[n+k]
        if first_chars == b'Wrote:   ':
            k = 9
            temp_string = ""
            while result[n+k] != b'\n':
                temp_string += result[n+k]
                k += 1
            written_files.append(temp_string)
    n += 1

# Dump output of garmintools run for analysis in case something goes wrong
file_log = open(path_log, 'w')
file_log.write(result)
file_log.close()

# For any newly written gmn files, save a copy as tcx format in directory specified
# by path_save_tcx so I can import to GoldenCheetah next time I start it.
devnull = open(os.devnull, 'w')
if not written_files:
    print "No new files to process."
else:
    print "New TCX files:"
for path_gmn in written_files:
    path_tcx = path_save_tcx + "/" + os.path.split(path_gmn)[1].replace(".gmn", ".tcx")
    print path_tcx
    file_tcx = open(path_tcx, 'w')
    subprocess.call(["gmn2tcx", path_gmn], stdout=file_tcx, stderr=devnull)
    file_tcx.close()
    os.chown(path_tcx, uid, gid)
devnull.close()

Files

Where possible, obtain the latest version of these files from their original creators. What follows is an archived copy of the versions I used when first setting this project up, in case the author’s pages disappear.