[chbot] Help with Python/HID

Mark Atherton markaren1 at xtra.co.nz
Wed Dec 25 18:34:24 GMT 2024


Thank you for that Synco; very similar to my current testing.

I got as far as making a simple fixture to test the I2C interface, to 
check that handle.write worked - which it did...

The light-bulb moment was the realization that the Python library will 
only ever attach to interface 0, which is where the I2C interface power 
up to. The UART is on interface 1...

Current thinking is to set DCNF1 high at power up (mode 2), then the 
UART will be moved to interface 0.

Adding one wire may do the trick; I already have a mode-change switch 
and timed-reset mechanism in place to swap config EEPROMs and restart 
the FT260.

Catch up soon.

-Mark




On 25-Dec-24 9:47 PM, Synco Reynders wrote:
> Hey Mark, are you still stuck on the issue. The old "have you tried 
> googling it?" is now "have you tried AI?"....
> Here is what Claude tells me:
> Let me help you debug this UART over HID communication issue with the 
> FT260. I notice a few potential issues and have some suggestions for 
> improvement.
> 
> First, let's analyze the issue:
> 
> 1. The main problem appears to be with the `handle.write()` call failing 
> (returning -1)
> 2. You're correctly using feature reports for configuration (0xA1 commands)
> 3. The data transmission format looks correct according to the protocol 
> (0xF0 header)
> 
> Here are some potential fixes:
> 
> 1. First, for HID writes, you typically need to prepend a Report ID byte 
> of 0x00. Try modifying your write call:
> 
> ```python
> bytes_written = handle.write([0x00, 0xF0, 0x01, 0x55])
> ```
> 
> 2. The FT260 might need some time to initialize after configuration. Add 
> a small delay:
> 
> ```python
> import time
> # After configuration
> time.sleep(0.1)  # 100ms delay
> ```
> 
> 3. You might want to add error checking and verification. Here's an 
> improved version of your code:
> 
> ```python
> import hid
> import time
> 
> FT260_VID = 0x0403
> FT260_PID = 0x6030
> 
> def check_ft260_status(handle, command):
>      bytes_read = handle.get_feature_report(0xA1, 64)
>      status = bytes_read[1] if len(bytes_read) > 1 else 0
>      return status == 0
> 
> try:
>      # Find and open device
>      handle = hid.device()
>      handle.open(FT260_VID, FT260_PID)
> 
>      # Reset UART
>      bytes_written = handle.send_feature_report([0xA1, 0x40])
>      if not check_ft260_status(handle, 0x40):
>          raise Exception("UART reset failed")
> 
>      # Wait for reset to complete
>      time.sleep(0.1)
> 
>      # Configure UART
>      config = [0xA1, 0x41, 0x04, 0x80, 0x25, 0x00, 0x00, 0x08, 0x00, 
> 0x00, 0x00]
>      bytes_written = handle.send_feature_report(config)
>      if not check_ft260_status(handle, 0x41):
>          raise Exception("UART configuration failed")
> 
>      # Set non-blocking mode
>      handle.set_nonblocking(True)
> 
>      # Send data with report ID
>      bytes_written = handle.write([0x00, 0xF0, 0x01, 0x55])
>      if bytes_written <= 0:
>          raise Exception(f"Data write failed: {bytes_written}")
> 
>      print(f"Successfully wrote {bytes_written} bytes")
> 
> except Exception as e:
>      print(f"Error: {str(e)}")
> finally:
>      handle.close()
>      print("FT260 device closed.")
> ```
> 
> 4. Also verify your UART configuration parameters:
>     - 0x04: UART interface
>     - 0x80, 0x25: Baud rate (9600)
>     - 0x00: Data bits (8)
>     - 0x00: Parity (None)
>     - 0x08: Stop bits (1)
>     - 0x00, 0x00: Flow control (None)
> 
> If these changes don't resolve the issue, you might want to:
> 
> 1. Check if the device is in the correct mode after reset using:
> ```python
> mode_report = handle.get_feature_report(0xA1, 64)
> print(f"Mode status: {[hex(x) for x in mode_report]}")
> ```
> 
> 2. Verify the USB permissions if you're on Linux (you might need to run 
> with sudo or add udev rules)
> 
> 3. Try different data lengths to see if there's a minimum payload size 
> requirement
> 
> Let me know if you need any clarification or if you'd like to try other 
> troubleshooting approaches.
> 
> 
> 
> 
> On Tue, 24 Dec 2024 at 19:06, Mark Atherton <markaren1 at xtra.co.nz 
> <mailto:markaren1 at xtra.co.nz>> wrote:
> 
>     Hi all,
> 
>     Help requested from Python newbie
> 
>     I am working on a simple code fragment to drive a UART over HID using
>     the FT260 in Python, but am having issues with writing data.
> 
>     Code snip below has had it's layout messed with, but is normally
>     indented OK.
> 
>     send_feature_report and get_feature_report appear to configure the
>     FT260
>     using Report IDs which are off type 'Feature'
> 
>     I don't appear to be able to send data using Report ID of type 'Output'
>     with handle.write, it returns returns -1 which is a failure.
> 
>     The payload is configured
>              0xF0 (send 1..4 bytes)
>              0x01 (payload length 1 byte)
>              0x55 (payload data)
> 
>     See AN_394_User_Guide_for_FT260.pdf
> 
>     help(hid) is also included below.
> 
>     Any help appreciated.
> 
>     -Mark
> 
> 
>     --------
> 
>     import hid
>     #import struct
> 
>     FT260_VID = 0x0403  # Default Vendor ID for FTDI
>     FT260_PID = 0x6030  # Default Product ID for FT260
> 
>     for device in hid.enumerate():
>         if device['vendor_id'] == FT260_VID and device['product_id'] ==
>     FT260_PID:
>           print(f"FT260 device found: {device['product_string']}")
> 
>     print("Opening FT260 device...")
>     handle = hid.device()
>     handle.open(FT260_VID, FT260_PID)
> 
>     bytes_written = handle.send_feature_report([0xA1,0x40])
>              # Reset UART
> 
>     bytes_read = handle.get_feature_report(report_num = 0xA1, max_length
>     = 64)
>     print(["%02x" % (each) for each in bytes_read]);
> 
>     handle.set_nonblocking(True);
> 
>     bytes_written =
>     handle.send_feature_report([0xA1,0x41,0x04,0x80,0x25,0x00,0x00,0x08,0x00,0x00,0x00])
>     print("wrote config ", bytes_written);
> 
>     bytes_written = handle.write([0xF0, 0x01, 0x55])
>     print("wrote data ", bytes_written);
> 
>     handle.close()
>     print("FT260 device closed.")
> 
>     ----
> 
>       >>> help(hid)
>     Help on module hid:
> 
>     NAME
>           hid
> 
>     CLASSES
>           builtins.object
>               device
> 
>           class device(builtins.object)
>            |  Device class.
>            |
>            |  A device instance can be used to read from and write to a HID
>     device.
>            |
>            |  Methods defined here:
>            |
>            |  __reduce__ = __reduce_cython__(...)
>            |
>            |  __setstate__ = __setstate_cython__(...)
>            |
>            |  close(self)
>            |      Close connection.
>            |
>            |      This should always be called after opening a connection.
>            |
>            |  error(self)
>            |      Get error from device, or global error if no device is
>     opened.
>            |
>            |      :return:
>            |      :rtype: str
>            |      :raises IOError:
>            |
>            |  get_feature_report(self, report_num, max_length)
>            |      Receive feature report.
>            |
>            |      :param report_num:
>            |      :type report_num: int
>            |      :param max_length:
>            |      :type max_length: int
>            |      :return: Incoming feature report
>            |      :rtype: List[int]
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  get_indexed_string(self, index)
>            |      Return indexed string.
>            |
>            |      :return:
>            |      :rtype: str
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  get_input_report(self, report_num, max_length)
>            |      Get input report
>            |
>            |      :param report_num:
>            |      :type report_num: int
>            |      :param max_length:
>            |      :type max_length: int
>            |      :return:
>            |      :rtype: List[int]
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  get_manufacturer_string(self)
>            |      Return manufacturer string (e.g. vendor name).
>            |
>            |      :return:
>            |      :rtype: str
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  get_product_string(self)
>            |      Return product string (e.g. device description).
>            |
>            |      :return:
>            |      :rtype: str
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  get_report_descriptor(self, max_length=4096)
>            |      Return the report descriptor up to max_length bytes.
>            |      If max_length is bigger than the actual descriptor, the
>     full descriptor will be returned.
>            |
>            |      :param max_length: Maximum number of bytes to read,
>     must be
>     positive
>            |      :type max_length: int
>            |
>            |      :return:
>            |      :rtype: List[int]
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError: If read error
>            |
>            |  get_serial_number_string(self)
>            |      Return serial number.
>            |
>            |      :return:
>            |      :rtype: str
>            |      :raises ValueError: If connection is not opened.
>            |      :raises IOError:
>            |
>            |  open(self, vendor_id=0, product_id=0, serial_number=None)
>            |      Open the connection.
>            |
>            |      :param vendor_id: Vendor id to connect to, default = 0
>            |      :type vendor_id: int, optional
>            |      :param product_id: Product id to connect to, default = 0
>            |      :type product_id: int, optional
>            |      :param serial_number:
>            |      :type serial_number: unicode, optional
>            |      :raises IOError:
>            |
>            |  open_path(self, path)
>            |      Open connection by path.
>            |
>            |      :param path: Path to device
>            |      :type path: bytes
>            |      :raises IOError:
>            |
>            |  read(self, max_length, timeout_ms=0)
>            |      Return a list of integers (0-255) from the device up to
>     max_length bytes.
>            |
>            |      :param max_length: Maximum number of bytes to read
>            |      :type max_length: int
>            |      :param timeout_ms: Number of milliseconds until timeout
>     (default: no timeout)
>            |      :type timeout_ms: int, optional
>            |      :return: Read bytes
>            |      :rtype: List[int]
>            |
>            |  send_feature_report(self, buff)
>            |      Accept a list of integers (0-255) and send them to the
>     device.
>            |
>            |      :param buff: Data to send (must be convertible into bytes)
>            |      :type buff: any
>            |      :return: Send result
>            |      :rtype: int
>            |
>            |  set_nonblocking(self, v)
>            |      Set the nonblocking flag.
>            |
>            |      :param v: Flag value (1 or 0, True or False)
>            |      :type v: int, bool
>            |      :return: Flag result
>            |      :rtype: int
>            |
>            |  write(self, buff)
>            |      Accept a list of integers (0-255) and send them to the
>     device.
>            |
>            |      :param buff: Data to write (must be convertible to
>     `bytes`)
>            |      :type buff: Any
>            |      :return: Write result
>            |      :rtype: int
>            |
>            |
>     ----------------------------------------------------------------------
>            |  Static methods defined here:
>            |
>            |  __new__(*args, **kwargs) from builtins.type
>            |      Create and return a new object.  See help(type) for
>     accurate signature.
> 
>     FUNCTIONS
>           __reduce_cython__(self)
> 
>           __setstate_cython__(self, __pyx_state)
> 
>           enumerate(vendor_id=0, product_id=0)
>               Return a list of discovered HID devices.
> 
>               The fields of dict are:
> 
>                - 'path'
>                - 'vendor_id'
>                - 'product_id'
>                - 'serial_number'
>                - 'release_number'
>                - 'manufacturer_string'
>                - 'product_string'
>                - 'usage_page'
>                - 'usage'
>                - 'interface_number'
> 
>               :param vendor_id: Vendor id to look for, default = 0
>               :type vendor_id: int, optional
>               :param product_id: Product id to look for, default = 0
>               :type product_id: int, optional
>               :return: List of device dictionaries
>               :rtype: List[Dict]
> 
>           version_str()
>               Return a runtime version string of the hidapi C library.
> 
>               :return: version string of library
>               :rtype: str
> 
>     DATA
>           __test__ = {}
> 
>     VERSION
>           0.14.0.post4
> 
>     FILE
> 
>     c:\users\user\appdata\local\programs\python\python37\lib\site-packages\hid.cp37-win_amd64.pyd
> 
>     _______________________________________________
>     Chchrobotics mailing list Chchrobotics at lists.ourshack.com
>     <mailto:Chchrobotics at lists.ourshack.com>
>     https://lists.ourshack.com/mailman/listinfo/chchrobotics
>     <https://lists.ourshack.com/mailman/listinfo/chchrobotics>
>     Mail Archives: http://lists.ourshack.com/pipermail/chchrobotics/
>     <http://lists.ourshack.com/pipermail/chchrobotics/>
>     Meetings usually 3rd Monday each month. See http://kiwibots.org
>     <http://kiwibots.org> for venue, directions and dates.
>     When replying, please edit your Subject line to reflect new subjects.
> 
> 
> 
> -- 
> Synco Reynders
>    m: +64 21 1825332
>    e: synco.reynders at gmail.com <mailto:synco.reynders at gmail.com>
> 
> _______________________________________________
> Chchrobotics mailing list Chchrobotics at lists.ourshack.com
> https://lists.ourshack.com/mailman/listinfo/chchrobotics
> Mail Archives: http://lists.ourshack.com/pipermail/chchrobotics/
> Meetings usually 3rd Monday each month. See http://kiwibots.org for venue, directions and dates.
> When replying, please edit your Subject line to reflect new subjects.




More information about the Chchrobotics mailing list