Page 1 of 1

Garmin USB sample program ported to PB

Posted: Tue Oct 06, 2009 12:25 am
by luis
Garmin I/O Software Development Kit sample program.

http://www8.garmin.com/support/commProtocol.html

This is a translation from the C sample program to PB.

I just bought a Garmin Forerunner and I'm investigating how to connect to it to exctract data.

Don't ask me questions on that because still I don't know anything ! Just downloaded the IOSDK.zip and started to convert the sample program to see what that would mean.

I'll look into the documentation in the future and hopefully will develop a complete sw layer to communicate with it.

Thought to share the sample code to help anyone else interested on the subject.

Bye!


Code: Select all


; Ported to PB by Luis on Oct, 5 2009 
; Compile as a CONSOLE application.
; Tested with a Garmin Forerunner 205 and PB 4.31 x86.
; On my unit it prints: "Forerunner205 Software Version 2.90"

; This is a 1:1 conversion from the original C code.
; The original C program with the PDF documentation can be found 
; here: http://www8.garmin.com/support/commProtocol.html

; ORIGINAL DISCLAIMER
; This file contains sample code to help application developers
; interface with Garmin USB units. This should not be viewed as a
; full implementation of PC to unit communication. It does not include
; error checking and other elements of a full implementation.
; Also, there are notes in the code suggesting other elements that
; might be necessary.


EnableExplicit

Global gHandle
Global gUSBPacketSize

Prototype p_SetupDiGetClassDevs (*ClassGuid, *Enumerator, hwndParent, flags.l) 
Global _SetupDiGetClassDevs.p_SetupDiGetClassDevs

Prototype p_SetupDiEnumDeviceInterfaces (DeviceInfoSet, *DeviceInfoData, *InterfaceClassGuid, MemberIndex.l, *DeviceInterfaceData) 
Global _SetupDiEnumDeviceInterfaces.p_SetupDiEnumDeviceInterfaces

Prototype p_SetupDiGetDeviceInterfaceDetail (DeviceInfoSet, *DeviceInterfaceData, *DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize.l, *RequiredSize, *DeviceInfoData)
Global _SetupDiGetDeviceInterfaceDetail.p_SetupDiGetDeviceInterfaceDetail

; from WINIOCTL.H
#FILE_DEVICE_UNKNOWN = $00000022
#FILE_ANY_ACCESS     = $0
#METHOD_BUFFERED     = $0

#MAX_BUFFER_SIZE    = 4096
#ASYNC_DATA_SIZE    = 64

Macro CTL_CODE(DeviceType, Function, Method, Access) ; from WINIOCTL.H
 (((DeviceType)<<16)|((Access)<<14)|((Function)<<2)|(Method))
EndMacro

#IOCTL_ASYNC_IN        = CTL_CODE (#FILE_DEVICE_UNKNOWN, $850, #METHOD_BUFFERED, #FILE_ANY_ACCESS)
#IOCTL_USB_PACKET_SIZE = CTL_CODE (#FILE_DEVICE_UNKNOWN, $851, #METHOD_BUFFERED, #FILE_ANY_ACCESS)


DataSection
 GUID_DEVINTERFACE_GRMNUSB: 
  Data.l $2c9c45c2
  Data.w $8e7d, $4c08
  Data.b $a1, $2d, $81, $6b, $ba, $e7, $22, $c0
EndDataSection

Structure SP_INTERFACE_DEVICE_DETAIL_DATA ; from SETUPAPI.H
 cbSize.l
 DevicePath.c[#ANYSIZE_ARRAY]
EndStructure

Structure SP_DEVINFO_DATA ; from SETUPAPI.H
 cbSize.l
 ClassGuid.GUID
 DevInst.l
 *Reserved 
EndStructure

Structure Packet_t
 mPacketType.c
 mReserved1.c
 mReserved2.w
 mPacketId.w
 mReserved3.w
 mDataSize.l
 mData.c[1]
EndStructure


Procedure SendPacket (*aPacket.Packet_t)

 Protected theBytesToWrite = SizeOf(Packet_t) - 1 + *aPacket\mDataSize
 Protected theBytesReturned.l ; dword
 
 WriteFile_(gHandle, *aPacket, theBytesToWrite, @theBytesReturned, 0)

 ; If the packet size was an exact multiple of the USB packet
 ; size, we must make a final write call with no Data
 
 If (theBytesToWrite % gUSBPacketSize) = 0  
    WriteFile_ (gHandle, 0, 0, @theBytesReturned, 0)
 EndIf

EndProcedure 


;-----------------------------------------------------------------------------
; Gets a single packet. Since packets may come simultaneously through
; asynchrous reads and normal (ReadFile) reads, a full implementation
; may require a packet queue and multiple threads.
;-----------------------------------------------------------------------------

Procedure.i GetPacket()

 Protected *thePacket.Packet_t 
 Protected theBufferSize.l ; DWORD
 Protected theBytesReturned.l ; DWORD
 Protected *theBuffer, *theNewBuffer
 
 Protected *theTempBuffer = AllocateMemory(#ASYNC_DATA_SIZE)
 
 Repeat 

    ; Read async data until the driver returns less than the
    ; max async data size, which signifies the end of a packet
          
    *theNewBuffer = 0;
    theBytesReturned = 0;

    DeviceIoControl_(gHandle, #IOCTL_ASYNC_IN, 0, 0, *theTempBuffer, #ASYNC_DATA_SIZE, @theBytesReturned, 0)

    theBufferSize + #ASYNC_DATA_SIZE
    
    *theNewBuffer = AllocateMemory(theBufferSize)
    
    If *theBuffer
        CopyMemory(*theBuffer, *theNewBuffer, theBufferSize - #ASYNC_DATA_SIZE)
    EndIf
    
    CopyMemory(*theTempBuffer, *theNewBuffer + theBufferSize - #ASYNC_DATA_SIZE, #ASYNC_DATA_SIZE)
    
    If *theBuffer
        FreeMemory(*theBuffer) : *theBuffer = 0
    EndIf

    *theBuffer = *theNewBuffer

    If theBytesReturned <> #ASYNC_DATA_SIZE     
        *thePacket = *theBuffer
        Break
    EndIf
    
 ForEver
 
 FreeMemory(*theTempBuffer)
 
 ; If this was a small "signal" packet, read a real
 ; packet using ReadFile
 
 If (*thePacket\mPacketType = 0 And *thePacket\mPacketId = 2)

    *theNewBuffer = AllocateMemory(#MAX_BUFFER_SIZE)
    
    theBytesReturned = 0

    FreeMemory(*thePacket)

    ; A full implementation would keep reading (and queueing)
    ; packets until the driver returns a 0 size buffer.
    
    ReadFile_ (gHandle, *theNewBuffer, #MAX_BUFFER_SIZE, @theBytesReturned, 0)

    ProcedureReturn *theNewBuffer
 Else   
    ProcedureReturn *thePacket
 EndIf
 
EndProcedure


Procedure Initialize()
 Protected hDll
 Protected theBytesReturned.l ; DWORD
 Protected *theDevDetailData.SP_INTERFACE_DEVICE_DETAIL_DATA
 Protected *thePacket.Packet_t
 Protected theDevInfoData.SP_DEVINFO_DATA 
 Protected theInterfaceData.SP_DEVICE_INTERFACE_DATA
 Protected theStartSessionPacket.Packet_t
 Protected theDevInfo 
 
 theDevInfoData\cbSize = SizeOf(SP_DEVINFO_DATA)
 theInterfaceData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)

 theStartSessionPacket\mPacketId = 5 ; id 

 hDll = OpenLibrary(#PB_Any, "Setupapi.dll")
 
 If hDll = 0
    Debug "Setupapi.dll failed."
    End
 EndIf
 
 _SetupDiGetClassDevs = GetFunction(hDll, "SetupDiGetClassDevsA")
 
 If _SetupDiGetClassDevs = 0
    Debug "SetupDiGetClassDevsA failed."
    End
 EndIf

 _SetupDiEnumDeviceInterfaces = GetFunction(hDll, "SetupDiEnumDeviceInterfaces")
 
 If _SetupDiEnumDeviceInterfaces = 0
    Debug "SetupDiEnumDeviceInterfaces failed."
    End
 EndIf
 
 _SetupDiGetDeviceInterfaceDetail = GetFunction(hDll, "SetupDiGetDeviceInterfaceDetailA")
 
 If _SetupDiGetDeviceInterfaceDetail = 0
    Debug "SetupDiGetDeviceInterfaceDetail failed."
    End
 EndIf
 
 theDevInfo = _SetupDiGetClassDevs (?GUID_DEVINTERFACE_GRMNUSB, 0, 0, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
 
 If _SetupDiEnumDeviceInterfaces (theDevInfo, 0, ?GUID_DEVINTERFACE_GRMNUSB, 0, @theInterfaceData) = 0
    If GetLastError_() = #ERROR_NO_MORE_ITEMS
        gHandle = 0
        ProcedureReturn 
    EndIf
 EndIf
 
 _SetupDiGetDeviceInterfaceDetail(theDevInfo, @theInterfaceData, 0, 0, @theBytesReturned, 0)
  
 *theDevDetailData = AllocateMemory(theBytesReturned);
 *theDevDetailData\cbSize = SizeOf(SP_INTERFACE_DEVICE_DETAIL_DATA)

 _SetupDiGetDeviceInterfaceDetail(theDevInfo, @theInterfaceData, *theDevDetailData, theBytesReturned, 0, @theDevInfoData)

 gHandle = CreateFile_(@*theDevDetailData\DevicePath, #GENERIC_READ | #GENERIC_WRITE, 0, 0, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL, 0)

 FreeMemory(*theDevDetailData)

 ; Get the USB packet size, which we need for sending packets
 DeviceIoControl_(gHandle, #IOCTL_USB_PACKET_SIZE, 0, 0, @gUSBPacketSize, SizeOf(gUSBPacketSize), @theBytesReturned, 0)
 
 ; Tell the device that we are starting a session.
 SendPacket(@theStartSessionPacket)

 ; Wait until the device is ready to the start the session
 Repeat
    *thePacket = GetPacket()

    If (*thePacket\mPacketType = 0 And *thePacket\mPacketId = 6) 
        Break
    EndIf

    FreeMemory(*thePacket) : *thePacket = 0
 ForEver     

 If *thePacket
    FreeMemory(*thePacket)
 EndIf
                 
 CloseLibrary(hDll)
EndProcedure



Procedure Main()

 Protected *thePacket.Packet_t
 Protected theProductDataPacket.Packet_t
  
 theProductDataPacket\mPacketType   = 20
 theProductDataPacket\mPacketId     = 254
 
 OpenConsole()
 
 Initialize()
 
 If gHandle = 0 
    PrintN("No device")
 EndIf

 ; Tell the device to send product data
 SendPacket (@theProductDataPacket)

 ; Get the product data packet
 Repeat  
    *thePacket = GetPacket();

    If (*thePacket\mPacketType = 20 And *thePacket\mPacketId = 255)
        Break
    EndIf

    FreeMemory(*thePacket)    
  ForEver

  ; Print out the product description
  PrintN(PeekS(@*thePacket\mData + 4))

  FreeMemory(*thePacket)
 
CloseConsole()
 
EndProcedure


Main()

Re: Garmin USB sample program ported to PB

Posted: Sat Oct 10, 2009 10:34 pm
by KarLKoX
Great, might be usefull for my ForeRunner 305 :)

Re: Garmin USB sample program ported to PB

Posted: Sat Oct 10, 2009 11:00 pm
by luis
KarLKoX wrote:Great, might be usefull for my ForeRunner 305 :)
The program is a little horrid but as I said is a direct translation of the original one !

If you can let me know if it works with your 305 too :)

Thank you.

Re: Garmin USB sample program ported to PB

Posted: Sun Oct 11, 2009 10:07 pm
by Michael Vogel
Thanks for doing such fine things, will try your code next week - will be interesting, if it also will do it's job for ANT devices as well...

If you have a Forerunner and use the Garmin Training Center as well, you may like to have a look at the following reporting and viewer tool at my home page...

Michael

Re: Garmin USB sample program ported to PB

Posted: Sun Oct 11, 2009 10:57 pm
by luis
Michael Vogel wrote:Thanks for doing such fine things, will try your code next week - will be interesting, if it also will do it's job for ANT devices as well...
Hi Michael, you are welcome.

ANT devices are by any chance the wireless ones? They used a USB "widget" to connect to the PC I believe... so MAYBE this software can works with them too... please let me know!
Michael Vogel wrote: If you have a Forerunner and use the Garmin Training Center as well, you may like to have a look at the following reporting and viewer tool
I did it already some months ago! It's quite impressive. I was thinking to make a little software me too (not 3D like yours) to have a tool simpler than the training center, doing only what I really need... and to learn something in the process because I don't know anything about GPS coordinates formats, satellite image retrieving / data overlay and such.

Now I have other things to do but maybe... in the near future!

Maybe I'll ask you some questions if you don't mind ... you certainly already know a lot more than me on the subject.

BTW: your software read the data from the file exported from the Training Center am I right ? Not DIRECTLY from the device.... ? Would you be interested in do so or it's irrelevant for you ?

Because this would be one of my main goals...

Re: Garmin USB sample program ported to PB

Posted: Mon Oct 12, 2009 11:31 pm
by Michael Vogel
luis wrote:ANT devices are by any chance the wireless ones?
Yes, the Forerunner 310XT does this proprietary wireless ANT protocol to a special USB stick - hope I'll have some time to check your program what will happen in such a constellation :?
luis wrote:Maybe I'll ask you some questions if you don't mind...
No problem, but don't expect to much :lol:
luis wrote:BTW: your software read the data from the file exported from the Training Center am I right ? Not DIRECTLY from the device.... ? Would you be interested in do so or it's irrelevant for you ?
This would be cool, I've thought to do so quite a while ago, but failed. So my program supports *.hst, *.tcx and *.GPX files only. A future version may accept *.FIT files (seems to be the next Garmin file format) and the direct import from the device -- if possible :wink:

Michael

Re: Garmin USB sample program ported to PB

Posted: Fri Oct 16, 2009 2:16 pm
by Michael Vogel
Had some time today, so I just tried the code on my Forerunner 310 and my Oregon 550...

• the first start of the program returned a "modulo by zero" error, so I added "If gUSBPacketSize" in line 90
• Oregon 550: "no device" appears and nothing else happens :|
• Forerunner 310: "no device" appears and nothing else happens :cry:

The program can't communicate with the ANT stick (software) of the Forerunner. And - more clear for me - there's no chance to talk to the Oregon, which acts like a connected drive...

Michael

Re: Garmin USB sample program ported to PB

Posted: Fri Oct 16, 2009 4:50 pm
by luis
Oh, it's a shame. When I'll dig deeper in the documentation I'll see if it's a limitation of the sample program or if ANT devices are really not supported through the USB driver ...

I'll keep this in mind, and in case I'll let you know.

uhm.. I have to check what I have installed by the way... I'll be back on this maybe through a PM because for now it's probably not very interesting for the others :)

Thank you for testing it.

Re: Garmin USB sample program ported to PB

Posted: Mon May 15, 2017 6:11 pm
by Michael Vogel
Hi Luis,

did you ever continued your investigations with garmin devices?

I wrote a (very simple) program which copies all activities from my forerunner when it is connected to my computer for charging.

There's another challenge, I would like to decode the fit files from the device. I think, there are two possible ways to do decoding with Purebasic:
1. using the FIT.dll from the SDK
2. understanding the file format to code all relevant parts in pure Purebasic

Started on the second path, I just did the first steps for now:
FIT file format poster

Re: Garmin USB sample program ported to PB

Posted: Mon May 15, 2017 6:25 pm
by luis
Hi Michael, wow is that post really from 2009 ? Apparently it is. Time flies.

No sorry, I didn't go beyond that simple test, probably something else came by or maybe I did lost interest in it, I don't remember :)