generic usb ttl uarts
Information Technology

Qt and USB – Pt. 7

generic usb ttl uartsIt took quite a bit of poking around to get as far as I am. Doing a whole bunch of training at a new client site and coming home with a brain of mush doesn’t help either. Adding insult to injury it seems that 90% of the “working code” posted on Stack Exchange is non-functioning feces which won’t even compile. When you need grown up answers, don’t go to Stack Exchange. It’s a place for 12 year old boys, not professional developers. When you ask a grown-up development question on there you get one of those little nasty grams from the admin along the lines of

some of the recent questions you’ve asked weren’t well received and didn’t get many views, you are in danger of losing the privilege to ask questions.

They prefer questions about stuff which exists in 60 different C/C++ books where 12 year old boys can Google an answer in under 30 seconds.

If you look carefully you will notice that I swapped the wire connections, effectively creating a null modem between the two USB UART bridges. At this point you can ls /dev to find out what ttyUSB devices exist, install the Linux screen program and with a line something like this,

screen /dev/ttyUSB0 19200 vt100

in two different terminal windows, one for each device, then type in one and see it appear in the other. Depending on your Linux distro you may need sudo in front of that or some other “run as root” qualifier.

Since I really hate unwashed rectal sphincters who post stuff like this and only pay lip service to the environment, here is a bit for you.

Latest KDE Neon based on Ubuntu 18.04 with all updates applied.

#!/bin/bash 
# additional dependancies for building Qt from scratch 
#  

sudo apt-get -f --assume-yes install g++ libgl1-mesa-dev libx11-dev \ 
libxss-dev git devscripts chrpath pax-utils squashfs-tools \ 
genisoimage curl whois synaptic  

#libgl1-mesa-glx kpat gnome-calculator 

sudo apt-get -f --assume-yes install bison build-essential gperf \ 
flex ruby python libasound2-dev libbz2-dev libcap-dev \ 
libcups2-dev libdrm-dev libegl1-mesa-dev libgcrypt11-dev libnss3-dev \ 
libpci-dev libpulse-dev libudev-dev \ 
libxtst-dev gyp meld 

# syslinux-utils 

sudo apt-get -f --assume-yes install libssl-dev libxcursor-dev \ 
libxcomposite-dev \ 
libxdamage-dev libxrandr-dev \ 
libfontconfig1-dev libxss-dev libcap-dev libxi-dev libxtst-dev re2c \ 
libdbus-1-dev libpci-dev libudev-dev \ 
gconf-2.0 gconf2 libgnome-keyring-dev libgnome-keyring-dev python-gtk2-dev \ 
libsnappy-dev  chromium-browser-l10n chromium-browser \ 
chromium-codecs-ffmpeg-extra  

# libre2-dev snapcraft 

-----

#!/bin/bash
# additional dependancies for building Qt from scratch on Ubuntu 18.04
#  

sudo apt-get -f --assume-yes install libssl1.0-dev

#!/bin/bash
# install editors and terminals
#  

sudo apt-get -f --assume-yes install bluefish jed \
qtcreator


I used synaptic to install the following:

roland@roland-HP-Compaq-Elite-8300-SFF:~/2019-analytics/doc$ sudo apt list --installed | grep libusb 
[sudo] password for roland:  

WARNING: apt does not have a stable CLI interface. Use with caution in scripts. 

libusb-1.0-0/bionic,now 2:1.0.21-2 amd64 [installed] 
libusb-1.0-0-dev/bionic,now 2:1.0.21-2 amd64 [installed]



roland@roland-HP-Compaq-Elite-8300-SFF:~$ sudo apt list --installed | grep codelite 

WARNING: apt does not have a stable CLI interface. Use with caution in scripts. 

codelite/bionic,now 10.0+dfsg-2 amd64 [installed] 
codelite-plugins/bionic,now 10.0+dfsg-2 amd64 [installed]

Yes, I could have used something besides CodeLite to do my development with, and so can you. When I started this I was on regular Ubuntu on a different machine so something like KDevelop would have forced installing about half (or so it seems like watching it install) of KDE. Yes, CodeLite has things which annoy me. The biggest annoyance is, unlike QtCreator, I can’t find a way to configure it to automatically save all modified source before performing a build and debug run.

testing uart screen shot

On the left is the terminal window created by running the program. On the right is the “screen” instance I tested against. The screen image has more than one test on it.

I have no idea what is causing the -9 errors trying to set up the device. I have rebooted multiple times, unplugged the devices and plugged them back in. This pipe error does not always occur. For an entire afternoon it didn’t happen no matter how many test runs I did. It also doesn’t seem to matter because the previously successful configuration either remains in place or is simply the default.

To be able to debug without creating udev rules I start CodeLite under sudo from a terminal. I chose not to do this with QtCreator because that used to really cock-up the environment so when you tried to run QtCreator as a mere mortal you got all kinds of protection errors. Never noticed that with CodeLite. Not saying it couldn’t happen, just saying I never noticed it.

Yes, some of the pieces of this code will look like like the hundred other “examples” you can find around the Web. The important parts aren’t. We will talk more about this code in another post.

 

/*
 * usb-test
 * 
 * Written by Roland Hughes as a stripped down test of basic libusb 1.0
 * communication. It is a very cleaned up version of an example found here:
 * https://stackoverflow.com/questions/14769107/libusb-bulk-transfer
 * 
 */
#include 
#include 
#include <sys/types.h>
#include 
#include 
#include <libusb-1.0/libusb.h>
#include 

#pragma pack(1)
struct CONTROL_MESSAGE
{
    struct libusb_control_setup ctrl_setup;
    uint32_t data;
};
#pragma pack()

const uint8_t IFC_ENABLE = 0X00;
const uint8_t SET_LINE_CTL = 0x03;
const uint8_t PURGE = 0x12;
const uint8_t SET_FLOW = 0x13;
const uint8_t SET_BAUDRATE = 0x1E;

/* 
 * Cygnal Integrated Products, Inc. CP210x
 * 
 *  idVendor           0x10c4 Cygnal Integrated Products, Inc.
 *  idProduct          0xea60 CP210x UART Bridge / myAVR mySmartUSB light
 *
 *       Endpoint Descriptor:
 *       bLength                 7
 *       bDescriptorType         5
 *       bEndpointAddress     0x81  EP 1 IN
 *       bmAttributes            2
 *         Transfer Type            Bulk
 *         Synch Type               None
 *         Usage Type               Data
 *       wMaxPacketSize     0x0040  1x 64 bytes
 * 
 *       Endpoint Descriptor:
 *       bLength                 7
 *       bDescriptorType         5
 *       bEndpointAddress     0x01  EP 1 OUT
 *       bmAttributes            2
 *         Transfer Type            Bulk
 *         Synch Type               None
 *         Usage Type               Data
 *       wMaxPacketSize     0x0040  1x 64 bytes
 *
 */
 
 /* 
  * Prolific 
  * Prolific Technology, Inc. PL2303 Serial Port
  * 
  *  idVendor           0x067b Prolific Technology, Inc.
  *  idProduct          0x2303 PL2303 Serial Port
  *
  *      bEndpointAddress     0x02  EP 2 OUT
  *      bmAttributes            2
  *        Transfer Type            Bulk
  *        Synch Type               None
  *        Usage Type               Data
  *      wMaxPacketSize     0x0040  1x 64 bytes
  *
  *      bEndpointAddress     0x83  EP 3 IN
  *      bmAttributes            2
  *        Transfer Type            Bulk
  *        Synch Type               None
  *        Usage Type               Data
  *      wMaxPacketSize     0x0040  1x 64 bytes
  *
  */
#define VENDOR_ID       0x10c4
#define PRODUCT_ID      0xea60

//  The OUT EP is where you write your output to the device
//
#define BULK_EP_OUT     0x01
#define OUT_PACKET_SIZE 0x0040

//  The IN EP is where YOU read FROM. Your device input comes from here
//
#define BULK_EP_IN      0x81
#define IN_PACKET_SIZE  0x0040

int interface_ref = 0;
int alt_interface,interface_number;

int print_configuration(struct libusb_device_handle *hDevice,
                        struct libusb_config_descriptor *config)
{
    char *data;
    int index;

    data = malloc(512);
    memset(data,0,512);

    index = config->iConfiguration;

    libusb_get_string_descriptor_ascii(hDevice,index,(unsigned char *)data,512);

    printf("\nInterface Descriptors: ");
    printf("\n\tNumber of Interfaces : %d",config->bNumInterfaces);
    printf("\n\tLength : %d",config->bLength);
    printf("\n\tDesc_Type : %d",config->bDescriptorType);
    printf("\n\tConfig_index : %d",config->iConfiguration);
    printf("\n\tTotal length : %d",config->wTotalLength);
    printf("\n\tConfiguration Value  : %d",config->bConfigurationValue);
    printf("\n\tConfiguration Attributes : %d",config->bmAttributes);
    printf("\n\tMaxPower(mA) : %d\n",config->MaxPower);

    free(data);
    data = NULL;
    return 0;
}

struct libusb_endpoint_descriptor* active_config(struct libusb_device *dev,struct libusb_device_handle *handle)
{
    struct libusb_device_handle *hDevice_req;
    struct libusb_config_descriptor *config;
    struct libusb_endpoint_descriptor *endpoint;
    int altsetting_index,interface_index=0;

    hDevice_req = handle;

    libusb_get_active_config_descriptor(dev,&config);
    print_configuration(hDevice_req,config);

    for(interface_index=0;interface_indexbNumInterfaces;interface_index++)
    {
        const struct libusb_interface *iface = &config->interface[interface_index];
        for(altsetting_index=0;altsetting_indexnum_altsetting;altsetting_index++)
        {
            const struct libusb_interface_descriptor *altsetting = &iface->altsetting[altsetting_index];

            int endpoint_index;
            for(endpoint_index=0;endpoint_indexbNumEndpoints;endpoint_index++)
            {
                endpoint = (struct libusb_endpoint_descriptor*)&altsetting->endpoint[endpoint_index];
                alt_interface = altsetting->bAlternateSetting;
                interface_number = altsetting->bInterfaceNumber;
            }

            printf("\nEndPoint Descriptors: ");
            printf("\n\tSize of EndPoint Descriptor : %d",endpoint->bLength);
            printf("\n\tType of Descriptor : %d",endpoint->bDescriptorType);
            printf("\n\tEndpoint Address : 0x0%x",endpoint->bEndpointAddress);
            printf("\n\tMaximum Packet Size: %x",endpoint->wMaxPacketSize);
            printf("\n\tAttributes applied to Endpoint: %d",endpoint->bmAttributes);
            printf("\n\tInterval for Polling for data Tranfer : %d\n",endpoint->bInterval);
        }
    }
    libusb_free_config_descriptor(NULL);
    return endpoint;
}

void init_and_set_protocol( libusb_device_handle *handle, uint32_t baud, 
                            uint8_t data_bits, uint8_t parity, uint8_t stop_bits)
{
    struct CONTROL_MESSAGE ctrl_msg;
    int e = 0;
    int length = sizeof(ctrl_msg);

    // purge any activity still on the device
    //
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, PURGE, 0b00001111, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e > 0)
    {
        printf("\nPurge successful!");
    }
    else
    {
        printf("\nError purging device e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
    
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, IFC_ENABLE, 1, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                LIBUSB_CONTROL_SETUP_SIZE, 5000);
    
    if(e > 0)
    {
        printf("\nEnable successful!");
    }
    else
    {
        printf("\nError Enabling device e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }

    //  SET_LINE_CONTROL
    //
    #pragma pack(1)
    union
    {
        struct LINE_CONTROL
        {
            uint8_t stop_bits   : 4;
            uint8_t parity      : 4;
            uint8_t word_length;
        } line_control;
        uint16_t wValue;
    } ctrl;
    #pragma pack()
    
    //  Yes, a production system would validate these values against a legal
    //  range, but this is not production.
    //
    ctrl.line_control.stop_bits = stop_bits;
    ctrl.line_control.parity = parity;
    ctrl.line_control.word_length = data_bits;
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, SET_LINE_CTL, ctrl.wValue, 1, 0);
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                LIBUSB_CONTROL_SETUP_SIZE, 5000);
    
    if(e == length)
    {
        printf("\nLine control set to %d %d %d\n", data_bits, stop_bits, parity);
    }
    else
    {
        printf("\nError setting line control  e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
    
    ctrl_msg.data = baud;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, SET_BAUDRATE, 0, 1, 4);
        
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e == length)
    {
        printf("\nBaud rate set to %d\n", baud);
    }
    else
    {
        printf("\nError setting baud rate  e = %d \n",e);
        printf("message: %s\n", libusb_strerror(e));
    }
}

int check_for_exit( char *txt)
{
    int ret_val = 0;
    
    if (txt != NULL)
    {
        // This is why everybody moved to C++. It's 2019 and we __still__
        // have to do left handed things to search a string in a case
        // insensitive manner.
        //
        char *sPtr = txt;
        while( *sPtr != '\0' )
        {
            *sPtr = toupper( ( unsigned char ) *sPtr );
            sPtr++;
        }

        if (strstr(txt, "EXIT") != NULL)
        {
            ret_val = 1;
        }
    }
    
    return ret_val;
}

int main(void)
{
    int r = 1;
    libusb_device **devs;
    libusb_device_handle *handle = NULL, *hDevice_expected = NULL;
    libusb_device *dev,*dev_expected;

    ssize_t cnt;
    int e = 0,config2;
    int i = 0;
    unsigned char str1[2048], str2[2048];
    char found = 0;

// Init libusb
    r = libusb_init(NULL);
    if(r < 0)
    {
        printf("\nfailed to initialise libusb\n");
        return 1;
    }
    else
    {
        printf("\nInit Successful!\n");
        
    }
    
    libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING);

// Get a list os USB devices
    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0)
    {
        printf("\nThere are no USB devices on bus\n");
        return -1;
    }
    printf("\nDevice Count : %ld\n-------------------------------\n",cnt);

    while ((dev = devs[i++]) != NULL  &&  !found)
    {
        struct libusb_device_descriptor desc = {0};
        r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0)
            {
            printf("failed to get device descriptor\n");
            libusb_free_device_list(devs,1);
            libusb_exit(NULL);
            break;
        }

        e = libusb_open(dev,&handle);
        if (e < 0) { printf("error opening device\n"); libusb_free_device_list(devs,1); libusb_exit(NULL); break; } printf("\nDevice Descriptors: "); printf("\nManufacturer index: %d", desc.iManufacturer); printf("\n\tVendor ID : %x",desc.idVendor); printf("\n\tProduct ID : %x",desc.idProduct); printf("\n\tSerial Number : %x",desc.iSerialNumber); printf("\n\tSize of Device Descriptor : %d",desc.bLength); printf("\n\tType of Descriptor : %d",desc.bDescriptorType); printf("\n\tUSB Specification Release Number : %d",desc.bcdUSB); printf("\n\tDevice Release Number : %d",desc.bcdDevice); printf("\n\tDevice Class : %d",desc.bDeviceClass); printf("\n\tDevice Sub-Class : %d",desc.bDeviceSubClass); printf("\n\tDevice Protocol : %d",desc.bDeviceProtocol); printf("\n\tMax. Packet Size : %d",desc.bMaxPacketSize0); printf("\n\tNo. of Configuraions : %d\n",desc.bNumConfigurations); e = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, (unsigned char*) str1, sizeof(str1)); if (e > 0)
        {
            printf("\nManufactured : %s",str1);
        }
        

        e = libusb_get_string_descriptor_ascii(handle, desc.iProduct, (unsigned char*) str2, sizeof(str2));
        if(e > 0)
        {
            printf("\nProduct : %s",str2);
        }

        printf("\n----------------------------------------");

        if(desc.idVendor == VENDOR_ID && desc.idProduct == PRODUCT_ID)
        {
            found = 1;
            break;
        }
        
        libusb_close(handle);      // only leave open the last one.
    }//end of while

    
    if(found == 0)
    {
        printf("\nDevice NOT found\n");
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return EXIT_FAILURE;
    }
    else
    {
        printf("\nDevice found");
        dev_expected = dev;
        hDevice_expected = handle;
    }

    e = libusb_get_configuration(handle,&config2);
    if(e!=0)
    {
        printf("\n***Error in libusb_get_configuration\n");
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return EXIT_FAILURE;
    }
    printf("\nConfigured value : %d",config2);

    if(config2 != 1)
    {
        libusb_set_configuration(handle, 1);
        if(e!=0)
        {
            printf("Error in libusb_set_configuration  %s\n", libusb_strerror(e));
            libusb_free_device_list(devs,1);
            libusb_close(handle);
            libusb_exit(NULL);
            return EXIT_FAILURE;
        }
        else
        {
            printf("\nDevice is in configured state!");
        }
    }

    libusb_free_device_list(devs, 1);

    int reAttacheDriver = 0;
    if(libusb_kernel_driver_active(handle, 0) == 1)
    {
        printf("\nKernel Driver Active");
        if(libusb_detach_kernel_driver(handle, 0) == 0)
        {
            reAttacheDriver = 1;
            printf("\nKernel Driver Detached!");
        }
        else
        {
            printf("\nCouldn't detach kernel driver!\n");
            libusb_free_device_list(devs,1);
            libusb_close(handle);
            return -1;
        }
    }

    e = libusb_claim_interface(handle, 0);
    if(e < 0)
    {
        printf("\nCannot Claim Interface");
        printf("\nMessage: %s", libusb_strerror(e));
        if (reAttacheDriver)
        {
            libusb_attach_kernel_driver(handle, 0);
        }
        libusb_free_device_list(devs,1);
        libusb_close(handle);
        libusb_exit(NULL);
        return -1;
    }
    else
    {
        printf("\nClaimed Interface\n");
    }

    active_config(dev_expected,hDevice_expected);
    printf("returned from active_config\n");
    //   Communicate
    
    init_and_set_protocol( handle, 19200, 8, 0, 1);

    char *my_string, *my_string1;
    int transferred = 0;
    int received = 0;
    int length = 0;

    puts("creating strings");
    my_string = (char *)malloc(OUT_PACKET_SIZE + 1);
    my_string1 = (char *)malloc(IN_PACKET_SIZE + 1);

    memset(my_string,'\0', OUT_PACKET_SIZE+1);
    memset(my_string1,'\0', IN_PACKET_SIZE+1);

    strcpy(my_string,"Roland Hughes\n");
    length = strlen(my_string);

    printf("\nTo be sent : %s\n",my_string);

    e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT),
            (unsigned char *)my_string,length,&transferred,5000);  // last param is timeout in ms
    if(e == 0 && transferred == length)
    {
        printf("\nWrite successful!");
        printf("\nSent %d bytes with string: %s\n", transferred, my_string);
    }
    else
    {
        printf("\nError in write! e = %d and transferred = %d\n",e,transferred);
        printf("message: %s\n", libusb_strerror(e));
    }

    int retry_count = 0;
    int exit_flag = 0;
    int rcvLength = IN_PACKET_SIZE;
    char current_line[2048];
    unsigned char new_line[] = {'\r', '\n', '\0'};
    unsigned char next_char[] = { ' ', '\0'};
    
    memset(current_line, '\0', sizeof(current_line));
    
    while( exit_flag == 0)
    {
        memset(my_string1, '\0', IN_PACKET_SIZE+1);
        e = libusb_bulk_transfer(handle, (BULK_EP_IN | LIBUSB_ENDPOINT_IN), 
                (unsigned char *)my_string1,rcvLength,&received,5000);
        if(e == 0)
        {
            for (int i=0; i < strlen(my_string1); i++) { if (my_string1[i] == '\r') { exit_flag = check_for_exit(current_line); printf("\n"); e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT), new_line,2,&transferred,5000); memset(current_line, '\0', sizeof(current_line)); } else { next_char[0] = my_string1[i]; putchar(my_string1[i]); e = libusb_bulk_transfer(handle, (BULK_EP_OUT | LIBUSB_ENDPOINT_OUT), next_char,1,&transferred,5000); strcat(current_line, (char *)next_char); } } fflush(stdout); } else { retry_count++; printf("retry_count %d\n", retry_count); if (retry_count > 10)
            {
                printf("retry_count %d\n", retry_count);
                exit_flag = 1;
            }
        }
    }

    //  Disable the device
    struct CONTROL_MESSAGE ctrl_msg;
    length = sizeof(ctrl_msg);
    
    ctrl_msg.data = 0;
    libusb_fill_control_setup((unsigned char *)&ctrl_msg, 0b01000001, IFC_ENABLE, 0, 1, 0);
    
    e = libusb_control_transfer(handle, ctrl_msg.ctrl_setup.bmRequestType,
                                ctrl_msg.ctrl_setup.bRequest,
                                ctrl_msg.ctrl_setup.wValue,
                                ctrl_msg.ctrl_setup.wIndex,
                                (unsigned char *)&ctrl_msg.data,
                                length, 5000);
    
    if(e == length)
    {
        printf("\nDisable successful!");
    }

    if (reAttacheDriver)
    {
        e = libusb_attach_kernel_driver(handle, 0);
        printf("Result of reattaching kernel driver: %d  msg: %s", e, libusb_strerror(e));
    }

    e = libusb_release_interface(handle, 0);

    libusb_close(handle);
    libusb_exit(NULL);

    printf("Hit return to continue\n");
    getchar();
    return 0;
}