openyou / libfitbit

Library for accessing and transfering data from the Fitbit health device
http://www.openyou.org
BSD 3-Clause "New" or "Revised" License
423 stars 66 forks source link

Fitbit times out on initial connect/ANT reset after first send #8

Closed TedHerman closed 13 years ago

TedHerman commented 13 years ago

Just starting with libfitbit, hope I can get this running. Documentation is impressive, especially the log/trace of the ANT protocol to the device. However, when I tried (both using latest download or git clone), there's a timeout error. Here's how it appears when I try:

root># python fitbit.py
--> ['a4', '01', '4a', '00', 'ef']

Traceback (most recent call last): File "fitbit.py", line 373, in sys.exit(main()) File "fitbit.py", line 294, in main device.init_tracker_for_transfer() File "fitbit.py", line 185, in init_tracker_for_transfer self.init_fitbit() File "fitbit.py", line 169, in init_fitbit self.init_device_channel([0xff, 0xff, 0x01, 0x01]) File "fitbit.py", line 173, in init_device_channel self.reset() File "/home/herman/fitbit/libfitbit/python/antprotocol/protocol.py", line 165, in reset self._check_reset_response() File "/home/herman/fitbit/libfitbit/python/antprotocol/protocol.py", line 141, in _check_reset_response data = self._receive() File "/home/herman/fitbit/libfitbit/python/antprotocol/libusb.py", line 77, in _receive r = self._connection.read(self.ep['in'], size, 0, self.timeout) File "/usr/lib/python2.6/site-packages/usb/core.py", line 637, in read self.__get_timeout(timeout) File "/usr/lib/python2.6/site-packages/usb/_debug.py", line 52, in do_trace return f(_args, *_named_args) File "/usr/lib/python2.6/site-packages/usb/backend/libusb10.py", line 493, in bulk_read timeout) File "/usr/lib/python2.6/site-packages/usb/backend/libusb10.py", line 593, in __read timeout)) File "/usr/lib/python2.6/site-packages/usb/backend/libusb10.py", line 357, in _check raise USBError(_str_error[retval.value]) usb.core.USBError: Operation timed out

NOTE: thinking this might be a machine-specific timing issue, I manually changed the timeout value from 1000 to 10000, but the result was the same. Any ideas where I am going wrong?

-- Thanks, Ted.

qdot commented 13 years ago

I still have this problem a lot too, in quite a few different situations (initial connect, post send, etc...). I've found that the protocol is very timing sensitive (that was a HUGE problem with bulk transfers), so I probably just need to work on figuring out what needs to be timed how.

TedHerman commented 13 years ago

If it makes any difference, here is the log of attaching the fit bit base station & then running fitbit.py:

 usb 4-1: new full speed USB device using uhci_hcd and address 5
 usb 4-1: New USB device found, idVendor=10c4, idProduct=84c4
 usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
 usb 4-1: Product: Fitbit Base Station
 usb 4-1: Manufacturer: Silicon Labs
 usb 4-1: SerialNumber: (long string, omitted in this posting)
 usb 4-1: reset full speed USB device using uhci_hcd and address 5
 usb 4-1: reset full speed USB device using uhci_hcd and address 5
 usb 4-1: reset full speed USB device using uhci_hcd and address 5

Running Linux version 2.6.37.6-smp (root@midas) (gcc version 4.5.2 (GCC) ) #2 SMP Sat Apr 9 23:39:07 CDT 2011

I didn't look to see if there are driver issues or timing parameters specifiable for the USB modules used.

TedHerman commented 13 years ago

One other thought. Can a device be used for read and write to fitbit? For instance, modprobe usbserial vendor=0x10c4 product=0x84c4 followed by plugging in the fitbit assigns /dev/ttyUSB0 to the fitbit. I see that libusb.py in antprotocol just has simple send/receive methods. Could these be read and write to the /dev/ttyUSB0 device, so I could try some things to debug the timeout problem? I tried a different computer today with the same timeout result.

qdot commented 13 years ago

There's a fork of libfitbit that has a pyserial class available, that will allow you to access the device via the tty route:

https://github.com/lawnjam/libfitbit/blob/master/python/antprotocol/pyserial.py

You'll want to pull just this file, as the rest of his fork is for working with the suunto t3 wrist watch, so it won't actually work with the fitbit. I'll try to bring this file into the library mainline ASAP too. Just need to set aside some testing time.

TedHerman commented 13 years ago

Never managed to get connected through pyserial on /dev/ttyUSB0, so I went back to investigate the libusb implementation in more detail. To do this, I recompiled libusb with debugging enabled. Apparently, there is communication, as you see below from the condensed log (removing most of the noise, just to show bytes transferred). Unfortunately the Python Ant protocol raises an error. Is this a version problem?

libusb:debug [bulk_transfer_cb] actual_length=0 --> ['a4', '09', '46', '00', '00', '00', '00', '00', '00', '00', '00', '00', 'eb'] libusb:debug [bulk_transfer_cb] actual_length=7 <-- ['a4', '03', '40', '00', '46', '00', 'a1'] --> ['a4', '03', '42', '00', '00', '00', 'e5'] <-- ['a4', '03', '40', '00', '42', '00', 'a5'] --> ['a4', '03', '43', '00', '00', '10', 'f4'] <-- ['a4', '03', '40', '00', '43', '00', 'a4'] --> ['a4', '02', '45', '00', '02', 'e1'] <-- ['a4', '03', '40', '00', '45', '00', 'a2'] --> ['a4', '02', '47', '00', '03', 'e2'] <-- ['a4', '03', '40', '00', '47', '00', 'a0'] --> ['a4', '02', '44', '00', 'ff', '1d'] <-- ['a4', '03', '40', '00', '44', '00', 'a3'] --> ['a4', '05', '51', '00', 'ff', 'ff', '01', '01', 'f0'] <-- ['a4', '03', '40', '00', '51', '00', 'b6'] --> ['a4', '01', '4b', '00', 'ee'] <-- ['a4', '03', '40', '00', '4b', '00', 'ac'] <-- ['a4', '09', '4e', '00', '99', 'b4', '00', '00', '00', '00', '00', '00', 'ce'] --> ['a4', '09', '4f', '00', '78', '01', '00', '00', '00', '00', '00', '00', '9b'] <-- ['a4', '09', '4e', '00', '99', 'b4', '00', '00', '00', '00', '00', '00', 'ce'] <-- ['a4', '03', '40', '00', '01', '05', 'e3'] --> ['a4', '09', '4f', '00', '78', '02', 'a6', '73', '00', '00', '00', '00', '4d'] <-- ['a4', '09', '4e', '00', '99', 'b4', '00', '00', '00', '00', '00', '00', 'ce'] <-- ['a4', '03', '40', '00', '01', '05', 'e3'] --> ['a4', '01', '4c', '00', 'e9'] <-- ['a4', '03', '40', '00', '4c', '00', 'ab'] --> ['a4', '09', '46', '00', '00', '00', '00', '00', '00', '00', '00', '00', 'eb'] <-- ['a4', '03', '40', '00', '46', '00', 'a1'] --> ['a4', '03', '42', '00', '00', '00', 'e5'] <-- ['a4', '03', '40', '00', '42', '15', 'b0'] Traceback (most recent call last): File "python/antprotocol/protocol.py", line 161, in _check_ok_response raise ANTStatusException("Message status %d does not match 0x0 (NO_ERROR)" % (status[5])) antprotocol.protocol.ANTStatusException: Message status 21 does not match 0x0 (NO_ERROR)

qdot commented 13 years ago

Info that I posted in my blog post at http://www.openyou.org/2011/08/07/libfitbit-hangups/ :

Whenever libfitbit connects to the tracker, the first transfer after plugging in the USB goes fine. We start communications with an ANT reset message, and we always get back a 0x6F package (Startup Message) with a payload of 0x00 (POWER_ON_RESET), which is what we expect since we just powered on the ANT stick. We then continue on through establishing a communications channel, running a beacon signal to find the tracker, and other steps as laid out in the fitbit protocol document.

The problem comes in on the second run of a libfitbit based utility. We try and reset the device, but instead of getting back a 0x6F packet, we usually receive something that looks like a bulk data receive packet, like we would expect back from a bank transfer. Further executions the utility will result in the same, until the point where the receive command from the reset completely times out, and continues to do so. The only way to fix things at this point is to unplug/replug the ANT stick, at which point things work fine again, for one round of communication.

The actual fitbit client doesn’t have to deal with this due to the fact that it grabs the device right when it starts up, and doesn’t let go until shutdown. I suppose I could try stopping and restarting the service with the base plugged in while watching an analyzer, and that may be my next step.

This bug is the major thing holding up library development right now. Until this is fixed, I can’t reliably run multiple sessions with the fitbit, and having to replug the USB stick isn’t a viable solution. I’m pretty sure I’m missing something about how connections should end or restart, but progress on this one is slow so far.

qdot commented 13 years ago

Grah. Fixed in local side code. We need a device reset when we first open the base, but it has to be done in a weird order. We need to set_configuration first (otherwise things die back in the ctypes for libusb), then reset, then set_configuration again. Just put it on a loop to test, works perfectly now, opening and closing. Will close out via commit.