[chbot] Help with Python/HID

Synco Reynders synco.reynders at gmail.com
Wed Dec 25 08:47:01 GMT 2024


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> 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
> 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.



-- 
Synco Reynders
  m: +64 21 1825332
  e: synco.reynders at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ourshack.com/pipermail/chchrobotics/attachments/20241225/5c8b5a28/attachment-0001.html>


More information about the Chchrobotics mailing list