Implementing Linux Support for Garmin 405CX

In an earlier post I described my solution for using a Garmin Forerunner 305 under Linux and uploading to Garmin Connect. I lost my GPS, and had to replace it with the 405. This post documents the communication interface and other technical details I used to implement Linux support for the 405 and possibly other ANT devices. You can find my implementation of ‘ANT Agent’ for Linux and user documentation on github. Unless you’re curious of how the device works or want to write code, you can skip this post and read install instructions.

This post is in two parts:

  1. First, some documentation regarding how to communicate with the device. Where possible I’ve tried to add details which were missing or divergent from specs.
  2. Second, the design of my software.

Again, this is a rather technical explanation. If just want to download data from your device skip to install instructions.

Communication Specifications:

The 405CX is an ANT wireless device. ANT defines a physical and link layer over the 2.4ghz ISM band. It allows for isochronous and and bulk data transfer. A connection to PC is established using the “Garmin USB ANT Stick”.

On top of ANT, the 405CX implements device discovery and authorization as defined by ANT-FS spec. Broadly ANT-FS defines an inter-op spec for file transfer, but the 405CX does not implement those features.

Once an ANT-FS transport is created, communication with the GPS device is done using the Garmin Device Interface SDK. Additionally, garmintools exist as a reference implementation.

References:

Specifications are published by Garmin and Dyanstream Innovations (ANT):

  1. ANT Basics: a series of YouTube videos introducing ANT wireless fundamental principals
  2. ANT Message Protocol and Usage: more fundamentals, but also defines message formats for communication with the “Garmin USB ANT Stick”
  3. ANT-FS: specification, registration required and information can not freely redistributed. Due to ANT-FS licensing, I’ll only cover what can be learned from other implementations: “gant” a.k.a. “garmin-ant-downloader”.
  4. Garmin Device Interfaces SDK: application layer protocol for communication with GPS device. Documentation is out-of-date (2006).

Protocol Stack:

The each of the succeeding sections document a layer of the protocol stack:

  1. Physical Communication with “USB ANT Stick”
  2. ANT Message Protocol
  3. ANT Channels
  4. Device Discovery
  5. Authorization and Paring
  6. Transport
  7. Garmin Device Interface

Physical Communication (antd/hw.py):

Communication with the “USB ANT Stick” is fairly simple. The device has one bulk endpoint 0x01/0x81 which can be used to send / receive ANT Messages.

The input endpoint should be continuously read by the PC to avoid the client device from overflowing. Should an overflow occur, the “USB ANT Stick” will begin discarding messages.

When writing to output endpoint, host can write data as fast as possible. A NACK is sent from the client when it is not ready to receive and host should retry.

In Linux there are at least two ways to access the hardware: usbtty via CONFIG_USB_SERIAL_GENERIC or libusb. Using CONFIG_USB_SERIAL_GENERIC a /dev/usbttyN file device is created where read() and write() simply perform bulk transfers. It is useful for prototyping, but I implemented against libusb for portability, multiple device support, and less user config.

ANT Message Protocol (antd.py/ant.py)

The ANT message protocol defines the binary packet format which read from and written to the USB endpoint. See “ANT Message Protocol and Usage”, section 9.3, for details.

ANT Channels

ANT channels are created and interacted with by sending and receiving ANT messages to/from the USB hardware, as documented in earlier sections.

An ANT channel is a connection over which isochronous and burst communication can be sent. To establish a channel all participants must have the same configuration. The required channel configuration can be captured by spying on USB packets sent from the windows app.

  • Frequence: 2450mhz
  • Message Period: 8hz
  • Network Key: a8a423b9f55e63c1

Once a channel is open, there are 3 kinds of communication possible:

  1. Broadcast: allows 8 bytes of data be transferred during the next transmission window (usually 8hz). Has lowest power requirements, but is unreliable, and client will have no idea if message was received successfully. The only broadcast message used in this implementation is the device discovery beacon transmitted from watch to PC.
  2. Acknowledged: allows 8 bytes of data to be transferred during next transmission window. The client will be notified if message was not successfully delivered. The application is responsible for implementing retry.
  3. Burst: allows for > 8 bytes of data be be transmitted. Burst messages begin transmitting at the next transmit window and continue in constant TX mode until all data is transmitted. Burst messages automatically retry up to 5 times.

In general when communicating with the 405CX if a message is > 8 bytes burst is used, otherwise acknowledged.

Device Discovery (antd/antfs.py)

The GPS device is constantly sending isochronous broadcast messages. The broadcast contains  a flag indicating if data is ready for download. The PC can use this broadcast and the DEVICE_NUMBER currently associated with the ANT channel to determine if it should connect with device.

The format of broadcast is:

0 x 43 00 00 00 00 00 00 00 00
       ^^ & mask 0x20 indicates device has data ready for download

Authorisation and Pairing (antd/antfs.py)

If the PC decides it wants to connect with a device, it needs to perform authorisation and pairing.

  1. Create a link with device. During this step, the PC and chooses a random frequency over which the devices will communicate. If the transfer aborts, subsequent attempts will  use different frequencies. This helps if RF environment is noisy.
  2. The PC requests the serial number of the device.
  3. If the serial number belongs to an unknown device, the PC must initiate pairing. This will force the user to acknowledged the connection on GPS watch. Once acknowledged, the watch will reply with a secret key that can be used for future connections.
  4. If the serial number belongs to a known device, we simply transmit the secret key.
  5. After completing either step 3 or 4, the device is in transport mode ready to being accepting commands.
1. create a link with device
; SEND-ACKNOWLEDGED 0 x 44 02 07 04 00 00 00 00
                              ^^ frequency, e.g. 0x07 = 2407mhz

2. request SN  of device
; SEND-ACKNOWLEDGED 0 x 44 03 01 00 00 00 00 00
; READ-BURST        0 x 43 ...
                    0 x 44 83 01 00 NN NN NN NN
                                    ^^ ^^ ^^ ^^ serial number of device
3. request pairing
; SEND-ACKNOWLEDGED 0 x 44 03 02 00 00 00 00 00
; READ-BURST        0 x 43 ...
; READ-BURST        0 x 44 83 01 08 00 00 00 00
                                 ^^ length of secret in bytes
; READ-BURST        0 x NN NN NN NN NN NN NN NN
                        ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ secret

4. transmit secret
; SEND-BURST        0 x 44 03 03 08 00 00 00 00
                                 ^^ length of secret in bytes
; SEND-BURST        0 x NN NN NN NN NN NN NN NN
                        ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ seret
; READ-BURST        0 x 43 ...
; READ-BURST        0 x 44 83 01 00 00 00 00 00
                              ^^ 0x01 = accepted key

Transport (antd/antfs.py)

At this point an ANT-FS transport is open. As I mentioned earlier the 405CX does not implement ANT-FS, so communication at this layer is undocumented. Fortunately, this layer is pretty thin, and simply applies wrapper around the Garmin Device Interface API.

1. Transmit to to device
; SEND-BURST        0 x 44 0D FF FF 00 00 NN NN
                                          ^^ ^^ size of burst transfer in 8 byte blocks (minus 1)
; SEND-BURST        0 x DD DD DD DD DD DD DD DD
                        ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ a Garmin packet as defined in
                                                "Garmin Device Interface SDK"

2. Receive data from device
; READ-BURST        0 x 43 ..
; READ-BURST        0 x 44 8D FF FF 00 00 NN NN
                                          ^^ ^^ size of burst of transfer in 8 byte blocks
; READ-BURST        0 x DD DD DD DD DD DD DD DD
                        ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ a Garmin packet as defined in
                                                "Garmin Device Interface SDK"

Garmin Device Interface (antd/garmin.py)

This layer is largely documented in the “Garmin Device Interface SDK” linked above. However the documentation hasn’t bee updated in at least 5 years.

  1. Some new data types are introduced. Some are documented by garmintools others are not.
  2. For those types that are undocumented, reverse engineering isn’t too hard. Typically fields are just appends to the end of older data types and can be ignored.
  3. Transfer of waypoints, and possibly other data types, has been changed to arrays (instead of message per element) presumably due performance over isochronous channel.

Code

I feel like I need to apologise for my previous implementation of 305 support. I hacked something together real quick and threw it up on my blog. More people wanted to use it then I ever expected, and I’m sure for most of them it was more trouble than it needed to be.

I hoped to fix this while implementing support for the 405. This projects still a bit experimental, but I’m using it everyday and it works very well.

Getting Support

 Implementation Details

The complete source code for this project can be found on github. It is a pure python implementation of the complete device communication stack documented in this post. This is my first real python project.

Tags: , ,

Comments are closed.