Garmin USB sample program ported to PB

Share your advanced PureBasic knowledge/code with the community.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Garmin USB sample program ported to PB

Post 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()
"Have you tried turning it off and on again ?"
A little PureBasic review
KarLKoX
Enthusiast
Enthusiast
Posts: 677
Joined: Mon Oct 06, 2003 7:13 pm
Location: France
Contact:

Re: Garmin USB sample program ported to PB

Post by KarLKoX »

Great, might be usefull for my ForeRunner 305 :)
"Qui baise trop bouffe un poil." P. Desproges

http://karlkox.blogspot.com/
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Garmin USB sample program ported to PB

Post 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.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Garmin USB sample program ported to PB

Post 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
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Garmin USB sample program ported to PB

Post 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...
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Garmin USB sample program ported to PB

Post 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
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Garmin USB sample program ported to PB

Post 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
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Garmin USB sample program ported to PB

Post 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.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Garmin USB sample program ported to PB

Post 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
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Garmin USB sample program ported to PB

Post 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 :)
"Have you tried turning it off and on again ?"
A little PureBasic review
Post Reply