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