ttwatch icon indicating copy to clipboard operation
ttwatch copied to clipboard

Support for Nike+ GPS Watch ?

Open prynhart opened this issue 8 years ago • 38 comments

Hi,

I've got a Nike+ SportWatch GPS "Powered by TomTom" (http://www.tomtom.com/lib/doc/Nike-SportWatch-QuickStartGuide-EN.pdf), and would love to be able to grab the raw GPS datafiles from it (prior to them being munged by the Nike+ Fat client and uploaded to Nike+).

I was hoping that it would really be a "TomTom" watch - but it seems that this isn't the case, as with the watch connected to a Ubuntu 10.04 LTS box, I'm getting:

# ttwatch -a Unable to open watch

Is there any chance that support could be added ? I'm hoping it's "close enough" to being a TomTom watch :-) I'm not much of a programmer, but am happy to help re USB sniffing etc.

This is how the device presents in my Mac (the output of system_profiler)

SportWatch:

  Product ID:   0x5455
  Vendor ID:    0x11ac
  Version:  2.00
  Serial Number:    105F94461E000B00
  Speed:    Up to 12 Mb/sec
  Manufacturer: Nike
  Location ID:  0xfa140000 / 8
  Current Available (mA):   500
  Current Required (mA):    500
  Capacity: 66 KB (66,048 bytes)
  Removable Media:  Yes
  Detachable Drive: Yes
  BSD Name: disk1
  Partition Map Type:   MBR (Master Boot Record)
  S.M.A.R.T. status:    Not Supported
  Volumes:
SportWatch:
  Capacity: 65 KB (65,024 bytes)
  Available:    62 KB (62,464 bytes)
  Writable: No
  File System:  MS-DOS FAT12
  BSD Name: disk1s1
  Mount Point:  /Volumes/SportWatch
  Content:  DOS_FAT_12
  Volume UUID:  F50B099E-1E3A-30BE-9AD7-474EBC44661B

Here's someones report (when they were playing around with this type of watch) for a University project: https://www.os3.nl/_media/2013-2014/courses/ccf/smartwatches-hristo-leendert.pdf

With Thanks in Advance

Patrick Rynhart

prynhart avatar Apr 03 '16 23:04 prynhart

Interesting. If you're feeling brave, you could edit libttwatch/libttwatch.h and change TOMTOM_VENDOR_ID and TOMTOM_MULTISPORT_PRODUCT_ID to the values of your Nike watch then recompile and try to use it. If you start with simple commands (like getting version information) then it shouldn't cause problems if it doesn't work exactly the same. From the document you linked to, it looks like it should work just fine, so you might be really lucky!

ryanbinns avatar Apr 06 '16 03:04 ryanbinns

I tried this today and it seems not to work. I executed ./ttwatch --version, but when send_packet(watch, MSG_UNKNOWN_0D, 0, 0, 20, 0) is called from ttwatch_send_startup_message_group, then libusb_interrupt_transfer(...) returns -1 (LIBUSB_ERROR_IO). I guess this means that the device is not supported? :-/

martinruenz avatar Jul 01 '16 09:07 martinruenz

Yeah that's the same point that I got to also - in libttwatch.cpp, this line returns LIBUSB_ERROR_IO:

result = libusb_interrupt_transfer(watch->device, write_usb_endpoint, packet, packet_size, &count, 10000);

called for packet 09 02 00 0D (dumped using print_packet(packet, packet_size)).

My guess is that the watch is the "same", but the addresses for write and read usb_endpoint are different than the official TomTom watches. Not sure how to determine these addresses - guess you would need to USB sniff the official Nike+ fat client....

prynhart avatar Jul 01 '16 09:07 prynhart

USB endpoints are fairly easy to find. Run lsusb -v -d11ac and look at the output. Look for an Interface Descriptor with bInterfaceClass equal to 3 Human Interface Device. There should be two Endpoint Descriptors under that. The addresses are listed as bEndpointAddress for each one. Simply copy these values to the code. You can also check the required packet size for the OUT endpoint - the wMaxPacketSize parameter.

Or if you want to list the output of lsusb -v -d11ac I can tell you which numbers to use.

ryanbinns avatar Jul 01 '16 11:07 ryanbinns

@ryanbinns I've tried a few things as suggested in the comments above, but failed to get it recognized with ttwatch.

Output from lsusb:

$ lsusb -v -d 11ac:5455

Bus 002 Device 005: ID 11ac:5455 Nike 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x11ac Nike
  idProduct          0x5455 
  bcdDevice            2.00
  iManufacturer           1 Nike
  iProduct                2 SportWatch
  iSerial                 3 REDACTED ;)
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           64
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          4 SportWatch
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              6 SportWatch HID
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      36
          Report Descriptor: (length is 36)
            Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
                            (null)
            Item(Local ): Usage, data= [ 0x01 ] 1
                            (null)
            Item(Main  ): Collection, data= [ 0x01 ] 1
                            Application
            Item(Global): Report ID, data= [ 0x01 ] 1
            Item(Global): Report Count, data= [ 0x3f ] 63
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Logical Minimum, data= [ 0x01 ] 1
            Item(Local ): Usage, data= [ 0x01 ] 1
                            (null)
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Report ID, data= [ 0x09 ] 9
            Item(Global): Report Count, data= [ 0x3f ] 63
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Logical Minimum, data= [ 0x01 ] 1
            Item(Local ): Usage, data= [ 0x01 ] 1
                            (null)
            Item(Main  ): Output, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              5 SportWatch Drive
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
Device Status:     0x0000
  (Bus Powered)

aw avatar May 11 '17 17:05 aw

It looks like it uses different endpoint addresses. I'm not sure if it will work, but you could try. Try replacing lines 225-238 of libttwatch.cpp with this:

write_usb_endpoint = 0x01;
read_usb_endpoint = 0x81;

Compile and try again. Don't do anything that writes to the watch though. Use -v to read version number to start with. If that works, you can be a little more adventurous, but still be careful at first.

ryanbinns avatar May 15 '17 06:05 ryanbinns

Sadly, I tried that and it still couldn't detect it.

aw avatar May 15 '17 07:05 aw

Hi Ryan (cc Alex)

Sorry for not replying - I did try a few things at the time, but got stuck. If I set up SSH access to the dev box I was using with the watch plugged in, do you think you might be able to have a look ? I'm not running much at the moment :-)

Thanks,

Patrick

On 15/05/17 7:34 PM, Alex Williams wrote:

Sadly, I tried that and it still couldn't detect it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ryanbinns/ttwatch/issues/72#issuecomment-301398776, or mute the thread https://github.com/notifications/unsubscribe-auth/APCDi-Qztzg0Q1D9S1mA9WyM6tlBZ-s9ks5r6AAjgaJpZM4H-stG.

prynhart avatar May 15 '17 07:05 prynhart

I don't think I'd be able to do anything useful. I think to make any more progress, you'd need to capture the USB packets transmitted between the watch and the Windows software when the watch is plugged in and the software does its stuff.

ryanbinns avatar May 23 '17 00:05 ryanbinns

Hi Ryan,

Apologies - I've been putting this off, because it sounded too difficult (and because I didn't have a bare-metal Windows box). However, I've just had a go now, and it's much easier than I thought.

I have captured using the latest build of USBPcap (1.2.0.2) using the current version of the Nike Connect+ Software (Version 6.6.34.141).

I thought we would start with the simplest possible case:

  • All drivers installed along with the Fat Client
  • The watch plugged in and empty (i.e. no runs)
  • Start the PCAP packet trace
  • Open the Fat Client, wait for it to communicate with the device, then close the Fat Client
  • Stop the packet trace

I've done this, and the PCAP capture file is attached. I've opened it okay in Wireshark and can see communication with the device.

Very happy to upload runs etc as needed (or to switch features on and off using the Fat Client under a packet trace).

Thanks again - and apologies for not following up sooner.

Patrick NikeWatchWithoutRuns.zip

prynhart avatar Aug 05 '17 23:08 prynhart

Oops - I did not mean to close this ticket :)

prynhart avatar Aug 05 '17 23:08 prynhart

Hi. Nike just announced they will be retiring the only applications that pull data off their watch by April 30, so I'm guessing this thread will receive more interest.

cgibson33 avatar Apr 19 '18 01:04 cgibson33

Yeah I know - amazing they can give less than 2 weeks notice on this - the link to the FAQ is https://en-gb-help.nike.com/app/answer/article/why-cant-i-sync/a_id/73653/country/nz

screen shot 2018-04-19 at 2 02 48 pm

prynhart avatar Apr 19 '18 02:04 prynhart

So, reading through the thread, it appears that you were able to capture some data, but that ryanbinns or someone else still needs some run data before being able to create a version of the software(s) that works with the Nike+ sportwatch? Correct me if I misunderstand.

cgibson33 avatar Apr 19 '18 20:04 cgibson33

Unfortunately the strap on my Nike+ GPS Sportwatch broke very recently and it won't connect up to any of my computers (a common fault with these watches). However, what you're saying above is correct - i.e. a patch needs to be created for the Nike version of this TomTom watch. Capturing the USB packets was straight forward on Windows (I followed the instructions at https://wiki.wireshark.org/CaptureSetup/USB) - so in theory anyone with a Windows box, the watch, and the Nike+ Connect Software (while it is still running) should be able to capture the protocol exchange between the client and the watch.

prynhart avatar Apr 19 '18 21:04 prynhart

Ok. I guess I better get on that then. I assume I need to run a variety of use cases. First I will try to capture watch connection with 1 run to transfer/upload.

cgibson33 avatar Apr 20 '18 01:04 cgibson33

I managed to capture a single run transfer. I have never used wireshark before, so I suspect that the file may also contain traffic from other USB ports. Any directions on how to remove information from the other ports?

cgibson33 avatar Apr 20 '18 13:04 cgibson33

The capture used the 64 bit version of wireshark with USBPcap included with the wireshark installation. Easy enough to use once I got it installed correctly. I suppose I should read the wireshark wiki on how to analyse the resultant file.

cgibson33 avatar Apr 20 '18 15:04 cgibson33

one run filtered for bus1 and device ID6.zip

Ok - I think I did that correctly - I did the capture and then created a new file filtered only for bus1 and device ID that correlated to the watch. I noticed that the address seemed to change from 1.6.0 to 1.6.1 but that appears to be the correct data/traffic.

cgibson33 avatar Apr 21 '18 00:04 cgibson33

Nice work. Not sure what the next step would be though - need someone who can look at the comms in the packet capture and compare this with what’s happening in the source. Would be good to get something underway before the shutdown, but at least these captures are around while the Nike client is still working.

Other things that could be captured while the client still works (probably with the runs empty) would be the settings - e.g. comms when the colours are inverted, weight of runner, enable/disable beep. I.e. All the control / admin features if the watch. Maybe do a trace for one at a time (so as little is changing as possible).

Get Outlook for iOShttps://aka.ms/o0ukef


From: cgibson33 [email protected] Sent: Saturday, April 21, 2018 12:56:40 PM To: ryanbinns/ttwatch Cc: Rynhart, Patrick; State change Subject: Re: [ryanbinns/ttwatch] Support for Nike+ GPS Watch ? (#72)

one run filtered for bus1 and device ID6.ziphttps://github.com/ryanbinns/ttwatch/files/1934020/one.run.filtered.for.bus1.and.device.ID6.zip

Ok - I think I did that correctly - I did the capture and then created a new file filtered only for bus1 and device ID that correlated to the watch. I noticed that the address seemed to change from 1.6.0 to 1.6.1 but that appears to be the correct data/traffic.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHubhttps://github.com/ryanbinns/ttwatch/issues/72#issuecomment-383255579, or mute the threadhttps://github.com/notifications/unsubscribe-auth/APCDi_94PH8f6qzWL5uO2FMprVvryecBks5tqoPIgaJpZM4H-stG.

prynhart avatar Apr 21 '18 02:04 prynhart

Will do. Do you think it would be ok if I left the watch plugged in the whole time, but just recorded each action separately using different file names? Also, I think there is a watch reset function that I should record because that might be the only way to clear the watch memory when it is getting full.

cgibson33 avatar Apr 21 '18 15:04 cgibson33

I think I should share my research notes. All the relevant information can be filtered by entering usb.capdata into wireshark's display filter — it's a leftover capture data that we're interested in. This leftover data is 64 bytes long. First byte is a direction (09 into watch, 01 from watch), second byte is a packet size, third one is just a counter. For the query that goes into watch, fourth byte is a command, next bytes are arguments. For the respond that we receive from watch fourth byte and next bytes are the reply data, last byte is some sort of check byte, it has to be the same as a counter byte.

The system nike client uses to download tracks is pretty dumb — it sends the '09 05 xx 10 00 00 00 ..' packet, the watch responds with a complete eeprom dump and the client is analyzing it afterwards. Typical response is '01 3d xx 01 xx xx xx (significant_data_bytes) xx' when we still have data to receive and '01 (size_of_packet) xx 00 xx xx xx (significant_data_bytes) 00 .. 00 xx' when it is the last packet.

It seems that I have a corrupted watch, so I couldn't analyze what I am getting from the watch.

P.S. I was still able to change some settings like sounds, weight, metric or imperial units, etc.

internecivusraptus avatar Apr 22 '18 09:04 internecivusraptus

Hi Ryan,

Recording the reset function sounds like an excellent idea :-) With regard to recording each action, not sure.  I guess it depends whether the Connect client sends / updates the watch immediately when a change is made.  (It probably does.). If you can see traffic (in wireshark afterwards) when you change/adjust a feature then this would be a good indicator as to whether you've got the exchange recorded.

The surefire / OCD way to do it would be to disconnect the watch each time, and record each transaction individually.  (i.e. Record, Connect Watch, Start client, Change one feature, Exit client, disconnect watch.). This would obviously be more noisy though.

In short - not sure which is the best approach :-)

Thanks,

Patrick

On 22/04/18 3:04 AM, cgibson33 wrote:

Will do. Do you think it would be ok if I left the watch plugged in the whole time, but just recorded each action separately using different file names? Also, I think there is a watch reset function that I should record because that might be the only way to clear the watch memory when it is getting full.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/ryanbinns/ttwatch/issues/72#issuecomment-383303636, or mute the thread https://github.com/notifications/unsubscribe-auth/APCDi6NYpuupqCos4DSoUyr-IAx60NxXks5tq0qYgaJpZM4H-stG.

prynhart avatar Apr 22 '18 23:04 prynhart

Very interesting.  I wonder if this 'offline' processing is being done because the watch itself doesn't support an API that can pull individual events off it ?  If so, the watch would be very different to the 'other' TomTom watches that this Github project supports.

On 22/04/18 9:57 PM, Igor Gritsenko wrote:

I think I should share my research notes. All the relevant information can be filtered by entering |usb.capdata| into wireshark's display filter — it's a leftover capture data that we're interested in. This leftover data is 64 bytes long. First byte is a direction (09 into watch, 01 from watch), second byte is a packet size, third one is just a counter. For the query that goes into watch, fourth byte is a command, next bytes are arguments. For the respond that we receive from watch fourth byte and next bytes are the reply data, last byte is some sort of check byte, it has to be the same as a counter byte.

The system nike client uses to download tracks is pretty dumb — it sends the '09 05 xx 10 00 00 00 ..' packet, the watch responds with a complete eeprom dump and the client is analyzing it afterwards. Typical response is '01 3d xx 01 xx xx xx (significant_data_bytes) xx' when we still have data to receive and '01 (size_of_packet) xx 00 xx xx xx (significant_data_bytes) 00 .. 00 xx' when it is the last packet.

It seems that I have a corrupted watch, so I couldn't analyze what I am getting from the watch.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/ryanbinns/ttwatch/issues/72#issuecomment-383369183, or mute the thread https://github.com/notifications/unsubscribe-auth/APCDi2WSNdb5cNx5VRDsXUqp1QVhPGWeks5trFP0gaJpZM4H-stG.

prynhart avatar Apr 22 '18 23:04 prynhart

Ok - last minute I recorded a bunch of settings interactions including a factory reset Documents.zip

cgibson33 avatar Apr 29 '18 15:04 cgibson33

When I did the settings changes, you could see packages transferring as soon as I typed any number or changed any setting on the fly, so I didn't bother removing the watch at any point

cgibson33 avatar Apr 29 '18 15:04 cgibson33

And Nike has checked out of the building - I assume that will get people's attention. capture

cgibson33 avatar May 01 '18 14:05 cgibson33

Ok - so now what? Do I need to do some further analysis on the captures? Is this watch too different than the other watches serviced by this code?

cgibson33 avatar May 04 '18 13:05 cgibson33

Hi guys, other solution would be to run a local proxy interpecting calls to Nike+ from the local app running and then store the data locally. But this is in case this project can't support the watch because as @prynhart suggested would be very different to the 'other' TomTom watches

iglezouton avatar May 06 '18 16:05 iglezouton

Hi guys - unfortunately nothing to add, I don't know how different this watch is. Hopefully someone else watching this thread can advise. Thanks, Patrick

prynhart avatar May 06 '18 23:05 prynhart