how to usb device serial output connect to teensy 4.0 usb

Looking the serial.ccp it seems this is hardcoded?

Code:
	case CDCACM:
		{
			println("Control - CDCACM LINE_CODING");
			setupdata[0] = 0;  // Setup baud rate 115200 - 0x1C200
			setupdata[1] = 0xc2;
			setupdata[2] = 0x1;
			setupdata[3] = 0;
		    setupdata[4] = 0; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits
		    setupdata[5] = 0; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space
		    setupdata[6] = 8; // Data bits (5, 6, 7, 8 or 16)
			mk_setup(setup, 0x21, 0x20, 0, 0, 7);
			queue_Control_Transfer(dev, &setup, setupdata, this);
			pending_control = 0x04;	// Maybe don't need to do...
			control_queued = true;
			return true;
		}

but later on there is

Code:
	// Now CDCACM
	if (sertype == CDCACM) {
		if (pending_control & 2) {
			pending_control &= ~2;
			// Should probably use data structure, but that may depend on byte ordering...
			setupdata[0] = (baudrate) & 0xff;  // Setup baud rate 115200 - 0x1C200
			setupdata[1] = (baudrate >> 8) & 0xff;
			setupdata[2] = (baudrate >> 16) & 0xff;
			setupdata[3] = (baudrate >> 24) & 0xff;
	        setupdata[4] = (format_ & 0x100)? 2 : 0; 	// 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits
	        setupdata[5] = (format_ & 0xe0) >> 5; 		// 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space
	        setupdata[6] = format_ & 0x1f;				// Data bits (5, 6, 7, 8 or 16)
	        print("CDCACM setup: ");
	        print_hexbytes(&setupdata, 7);
			mk_setup(setup, 0x21, 0x20, 0, 0, 7);
			queue_Control_Transfer(device, &setup, setupdata, this);
			control_queued = true;
			return;
		}

Is it enough to do the userial.begin(baud, format ); Should some init or claim be also done? what does the Control - 0x21,0x22, 0x3 mean?

I just do not trust the serial interface gets fully set up on the serial example or my code that follows it.

OK I will end this monologue now, and focus to something else.
 
Last edited:
Well I did still one more thing and should have done that also long time ago.

That is made a simple LabView program to send and receive data. This is on Windows and just communicating via the serial port (COM3) Nothing fancy, just sending and receiving data, baud rate set to be 115200.

I get it initialised the same as with Teensy 4.1 USB Host, but I can also send the commands and receive a response that looks correct format, and the devise stays on. So that is promising.

I will next test on LabView environment setting up the communication a bit more and see if I can actually get commands executed with expected result.

As it is it looks pretty sure something does not get quite right initialised on the teensy environment. It is strange though that I can send and receive the first initialisation (ASCII text)

EDIT, with the LabView code I got just button colour controlled, so the control works, and it does not even need any extra initialisation.
 
Last edited:
So I will still continue this monologue.

With Windows 10 LabView code the LoupeDeck works as expected (and commands are simple, the few ones I tested)

With Teensy 4.1 USB Host it completes the first handshake, but after that does not accept new messages.

I do think it is related to this https://electronics.stackexchange.com/questions/253669/usb-cdc-help-with-zero-packets

With WireShark I can see there is empty messages when it is communicating with its own SW, and only can think that those are the end of packet message. Is there any way to send these empty messages?

The other thing I could think of that the driver is not fully correct, but why would it do the first handshake then.

The first handshake is different than the following. It is sending formal long ASCII text, so I think they just detect everything needed was received. The following messages are short binary messages (and there is also long ones) Knowing when header ends and data starts and ends for sure makes things much easier. I do think they use the empty messages for this and somehow Windows can sends them? but not the USBHost_t36.h


Here is the printout, after the last TX it should send data, but goes to sleep after few seconds.

Code:
USB Host Testing - Serial
USB2 PLL running
 reset waited 6
USBHS_ASYNCLISTADDR = 0
USBHS_PERIODICLISTBASE = 20003000
periodictable = 20003000
port change: 10001803
    connect
  begin reset
port change: 18001205
  port enabled
  end recovery
new_Device: 480 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
Device Descriptor:
  12 01 00 02 EF 02 01 40 C2 2E 04 00 00 01 01 02 03 01 
    VendorID = 2EC2, ProductID = 0004, Version = 0100
    Class/Subclass/Protocol = 239 / 2 / 1
    Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: Loupedeck
enumeration:
Product: Loupedeck Live
enumeration:
Serial Number: LDD2001014022200100398B2002
enumeration:
Config data length = 75
enumeration:
Configuration Descriptor:
  09 02 4B 00 02 01 04 A0 32 
    NumInterfaces = 2
    ConfigurationValue = 1
  08 0B 00 02 02 02 00 05 
    Interface Association = 0 through 1
    Class / Subclass / Protocol = 2 / 2 / 5
  09 04 00 00 01 02 02 01 05 
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 2 / 2 / 1
  05 24 00 10 01 
  05 24 01 03 01 
  04 24 02 06 
  05 24 06 00 01 
  07 05 82 03 40 00 07 
    Endpoint = 2 IN
    Type = Interrupt
    Max Size = 64
    Polling Interval = 7
  09 04 01 00 02 0A 00 00 06 
    Interface = 1
    Number of endpoints = 2
    Class/Subclass/Protocol = 10 / 0 / 0
  07 05 01 02 00 02 01 
    Endpoint = 1 OUT
    Type = Bulk
    Max Size = 512
    Polling Interval = 1
  07 05 81 02 00 02 01 
    Endpoint = 1 IN
    Type = Bulk
    Max Size = 512
    Polling Interval = 1
enumeration:
USBHub memory usage = 960
USBHub claim_device this=200045A0
USBSerial(512)claim this=20003080
vid=2EC2, pid=4, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1
08 0B 00 02 02 02 00 05 09 04 00 00 01 02 02 01 05 05 24 00 10 01 05 24 01 03 01 04 24 02 06 05 24 06 00 01 07 05 82 03 40 00 07 09 04 01 00 02 0A 00 00 06 07 05 01 02 00 02 01 07 05 81 02 00 02 01 
Descriptor 11 = IAD
Descriptor 4 = INTERFACE
USBSerial(512)claim this=20003080
vid=2EC2, pid=4, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1
09 04 00 00 01 02 02 01 05 05 24 00 10 01 05 24 01 03 01 04 24 02 06 05 24 06 00 01 07 05 82 03 40 00 07 09 04 01 00 02 0A 00 00 06 07 05 01 02 00 02 01 07 05 81 02 00 02 01 
Descriptor 36 =  ???
Descriptor 36 =  ???
Descriptor 36 =  ???
Descriptor 36 =  ???
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
USBSerial(512)claim this=20003080
vid=2EC2, pid=4, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1
09 04 01 00 02 0A 00 00 06 07 05 01 02 00 02 01 07 05 81 02 00 02 01 
len = 23
USBSerial, rxep=1(512), txep=1(512)
  rx buffer size:1024
  tx buffer size:1024
new_Pipe
new_Pipe
Control - CDCACM LINE_CODING
Descriptor 5 = ENDPOINT
Descriptor 5 = ENDPOINT
control callback (serial) 4
Control - 0x21,0x22, 0x3
control callback (serial) 0
///////////////////////////////////////////////////////
*** Device USB_serial 2ec2:4 - connected ***
  manufacturer: Loupedeck
  product: Loupedeck Live
  Serial: LDD2001014022200100398B2
control callback (serial) 6
CDCACM setup: 00 C2 01 00 00 00 08 
control callback (serial) 4
Control - 0x21,0x22, 0x3
USB_serial started

Request1 WS 

GET /index.html
HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

control callback (serial) 0
txtimer
  TX data (116) 47 45 54 20 2F 69 6E 64 65 78 2E 68 74 6D 6C 0D 0A 48 54 54 50 2F 31 2E 31 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 55 70 67 72 61 64 65 0D 0A 55 70 67 72 61 64 65 3A 20 77 65 62 73 6F 63 6B 65 74 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 74 2D 4B 65 79 3A 20 64 47 68 6C 49 48 4E 68 62 58 42 73 5A 53 42 75 62 32 35 6A 5A 51 3D 3D 0D 0A 0D 0A 00 
tx1:
rx token: 817F8100 transfer length: 512 len:129 - 48 54
rx: 48 54 54 50 2F 31 2E 31 20 31 30 31 20 53 77 69 74 63 68 69 6E 67 20 50 72 6F 74 6F 63 6F 6C 73 0D 0A 55 70 67 72 61 64 65 3A 20 77 65 62 73 6F 63 6B 65 74 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 55 70 67 72 61 64 65 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 74 2D 41 63 63 65 70 74 3A 20 73 33 70 50 4C 4D 42 69 54 78 61 51 39 6B 59 47 7A 7A 68 5A 52 62 4B 2B 78 4F 6F 3D 0D 0A 0D 0A 

Response1 

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

TEST 

START 

txtimer
  TX data (6) 82 87 00 00 00 00 
tx1:
txtimer
  TX data (7) 07 02 0F 0A FF 00 00 
tx1:
DONE


Code:
#include <USBHost_t36.h>
#define USBBAUD 115200 //2096000
uint32_t baud = USBBAUD;
uint32_t format = USBHOST_SERIAL_8N1;
uint32_t Timer;


char      WS_UPGRADE_HEADER[] = "GET /index.html\r\n"
                                "HTTP/1.1\r\n"
                                "Connection: Upgrade\r\n"
                                "Upgrade: websocket\r\n"
                                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";
                                

bool      FistByteReceived = false;


uint8_t SHORT_HEADER[]={0x82, 0x80, 0x00, 0x00, 0x00, 0x00};


USBHost USB_SER;
USBHub hub1(USB_SER);
USBSerial_BigBuffer USB_serial(USB_SER, 1); // Handles anything up to 512 bytes



USBDriver *drivers[] = {&hub1, &USB_serial};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "USB_serial" };
bool driver_active[CNT_DEVICES] = {false, false};

void setup()
{
  while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB Host Testing - Serial");
  USB_SER.begin();
  delay(1000);
}


void loop()
{
  
  if((micros() - Timer)>1000){
      Timer=micros();
      USB_SER.Task();
      // Print out information about different devices.
      for (uint8_t i = 0; i < CNT_DEVICES; i++) {
        
        if (*drivers[i] != driver_active[i]) {
          Serial.println("///////////////////////////////////////////////////////");
          if (driver_active[i]) {
            Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
            driver_active[i] = false;
            FistByteReceived = false;
    
          } else {
            Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
                
            driver_active[i] = true;
    
            const uint8_t *psz = drivers[i]->manufacturer();
            if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
            psz = drivers[i]->product();
            if (psz && *psz) Serial.printf("  product: %s\n", psz);
            psz = drivers[i]->serialNumber();
            if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
    
            // If this is a new Serial device.
            if (drivers[i] == &USB_serial) {  USB_serial.begin(baud, format ); Serial.println("USB_serial started");}
           
          }
        }
      }
  }


if (FistByteReceived == false){
      //this goes out ok
      Serial.println("\r\nRequest1 WS \r\n");
      Serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));
      USB_serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));
      delay(200);
      //we get expected response
      Serial.println("\r\nResponse1 \r\n");
      while (USB_serial.available()) {Serial.write(USB_serial.read());  FistByteReceived = true; }

   
      Serial.println("TEST \r\n");

    
      uint8_t   COMMAND[] = { 0x07, 0x02, 0x0f, 0x0a, 0xff, 0x00, 0x00 };//button 10 red
      
      
      
      SHORT_HEADER[1] = 0x80+sizeof(COMMAND );
       
       Serial.println("START \r\n");


       for (uint16_t i = 0; i < sizeof(SHORT_HEADER); i++) {USB_serial.write((uint8_t)SHORT_HEADER[i]); }
        delay(10);
      
       for (uint16_t i = 0; i < sizeof(COMMAND     ); i++) {USB_serial.write((uint8_t)COMMAND     [i]); }
       delay(10);
     

      // USB_serial.write((uint8_t)SHORT_HEADER , sizeof(SHORT_HEADER ));  
      // USB_serial.write((uint8_t)COMMAND , sizeof(COMMAND ));
       //for (uint16_t i = 0; i < 512-(sizeof(SHORT_HEADER )+sizeof(COMMAND ) ); i++) {USB_serial.write((uint8_t)0x00); }
       delay(10);
       
       
       while (USB_serial.available()) {Serial.print("RECEIVED2: ");Serial.print(USB_serial.read(), HEX);   }
       delay(100);
       //just making sure we did not miss any incoming data, and nothing received
       while (USB_serial.available()) {Serial.print("RECEIVED3: ");Serial.print(USB_serial.read(), HEX);   }
       Serial.println("DONE \r\n");

      }





      while (USB_serial.available()) {Serial.print(USB_serial.read(), HEX);  FistByteReceived = true; }


}
 
BTW, this trickers "*** Empty ***"

for (uint16_t i = 0; i < 512; i++) {USB_serial.write((uint8_t)0x00); }

Why is that if it should be able to handle 512 bytes?

I guess it is this from serial.ccp

Code:
void USBSerialBase::timer_event(USBDriverTimer *whichTimer)
{
	debugDigitalWrite(7, HIGH);
	println("txtimer");
	uint32_t count;
	uint32_t head = txhead;
	uint32_t tail = txtail;
	if (head == tail) {
		println("  *** Empty ***");
		debugDigitalWrite(7, LOW);
		return; // nothing to transmit

So not sending empty message, but nothing.

Code:
size_t USBSerialBase::write(uint8_t c)
{
	if (!device) return 0;
	uint32_t head = txhead;
	if (++head >= txsize) head = 0;
	while (txtail == head) {
		// wait...
	}

Maybe a problem in here, but do not understand it.

I do think the CDCACM implementation on USBHost_t36 is missing some flow control stuff Loupedeck binary data transfers are expecting ACK/NAK etc.. Would need some support with that to get this working.
 
Last edited:
Maybe this? https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf

Code:
42.5.3.11 Ping Control
USB 2.0 defines an addition to the protocol for high-speed devices called Ping. Ping is required for all USB 2.0 High-speed bulk and control endpoints.
Ping is not allowed for a split-transaction stream. This extension to the protocol eliminates the bad side-effects of Naking OUT endpoints. The Status field has a Ping State bit, which the host controller uses to determine the next actual PID it uses in the next transaction to the endpoint (see the table below).
The Ping State bit is only managed by the host controller for queue heads that meet the following criteria:
• Queue head is not an interrupt and • EPS field equals High-Speed and • PIDCode field equals OUT
The following table illustrates the state transition table for the host controller's responsibility for maintaining the PING protocol. Refer to Chapter 8 in the USB Specification Revision 2.0 for detailed description on the Ping protocol.
 
ACK, NYET (ping protocol), NAK tokens are handled automatically by the EHCI hardware, so you won't see much in the source code regarding these low-level protocol details.

Likewise for split transactions, where the host communicates at 480 Mbit/sec to a transaction translator which in turn communicates with the device at 12 or 1.5 Mbit/sec speed, most of the low-level tokens stuff of USB protocol is handled automatically by the hardware. However, for the periodic schedule (interrupt and isochronous protocols) the EHCI hardware does depend on the software to configure in which of the 8 micro frames to start and attempt to complete the slower speed operation the TT performs. So you will see a bunch of code which plans the bandwidth usage, but like with ACK, NYET, NAK, you won't code which gets involved in the actual handling of those tokens. The division of labor puts pretty much all that very low-level stuff as responsibility of the hardware and the software is responsible for managing in-memory linked list structures (circular list for asynchronous protocols, inverse tree for periodic protocols) which specify at a pretty high level what work the hardware is expected to perform.

This is all explained in the EHCI 1.0 spec. The IMXRT1062 reference manual has a lot of the EHCI spec info more or less copied. If you're used to USB code which deals with USB protocol at the token level, which is pretty common on most microcontrollers, it can be difficult to wrap your head around EHCI where the hardware does so much more. Instead of toggling a DATA0/1 bit after each ACK and using a pair of buffers, where your code needs to respond to each DATA0 / DATA1 token (or "packet") or explicitly tell the hardware to respond with NAK or STALL, the EHCI hardware works at a much higher level where you give it pointers to a buffer up to 16 or 20 kbytes and the hardware automatically divys it into many DATA0 / DATA1 packets, doing all the ACK / NYET / NAK processing automatically, with optionally an interrupt after the whole thing is completed (or abandoned after retrying a number of times).

EHCI is rather complicated, as I'm sure anyone who tries reading the spec would agree. I spent nearly 1 year before I managed to get it working, and that was with the benefit of being able to study how Linux does things. And even now we're not supporting isochronous. The hardware is so powerful and does so much automatically that it's difficult to think about how it all works down to the low-level protocol details you'll see in chapter 8 of the USB 2.0 spec. Just know that the you won't see much code specifically dealing with the ping protocol (ACK vs NYET) and other chapter 8 stuff because with EHCI that low level work is almost all done inside the hardware and software is responsible for managing higher level work queues.
 
The question I have are these all configured to work* and if they are, what is the problem I am having.

I have now ensured that on Windows environment there is no problem communicating with the device (sending the same data as on Teensy environment with my own code that does nothing else). So the device seems to be happy with what ever the Windows USB Serial drivers are doing.

I can receive data on Teensy USB Host environment and the device responds to the first ASCII based message as expected.

But after when trying to use the binary type messages, I get no response from device (I see on Windows environment I should get) So I can only assume that the device is waiting for some handshake before acepting the binary package (but maybe did not use that for the ASCII, for what ever reason) When it does not get what it is expecting it gets to sleep. (same happens on Windows environment if sending wrongly structured data)

On Windows environment the device communicating with the Loupedeck SW using Wireshark I can see that there is somekind of handshaking between the data packages both direction, looks like empty message. (similar start bytes as with data, but no data) see https://drive.google.com/file/d/1i96Vabbc5HP7idVxVJb53NmdOWDSdI2n/view?usp=share_link

It could be also something wrong on my code, it is posted couple of posts up.


*I mean configured to work as the Loupedeck device expect. On the Teensy 3.6 documentation I can see for example that


TX endpoint Type

NOTE: When only one endpoint (RX or TX, but not both) of an endpoint pair is used, the unused endpoint should be configured as a bulk type endpoint.

00 Control
01 Isochronous
10 Bulk (I assume should be this for Loupedeck, but just guessing)
11 Interrupt

Can be configured, but do not know if relevant, how it is configured, how it should be configured, and does it work the same with Teensy 4.1 I am now working. Maybe should test with Teensy 3.6?
 
Last edited:
I tested with Teensy 3.6, works just the same as 4.1.

Note this is rotating encoder knob, the header and data is separated.

8255103FFrx token: 1FE8100 transfer length: 512 len:2 - 82 5
rx: 82 05
rx token: 81FB8100 transfer length: 512 len:5 - 5 1
rx: 05 01 00 03 FF
8255103FFrx token: 1FE8100 transfer length: 512 len:2 - 82 5
rx: 82 05
rx token: 81FB8100 transfer length: 512 len:5 - 5 1
rx: 05 01 00 03 FF
8255103FFrx token: 1FE8100 transfer length: 512 len:2 - 82 5
rx: 82 05
rx token: 81FB8100 transfer length: 512 len:5 - 5 1
rx: 05 01 00 03 FF
8255103FFrx token: 1FE8100 transfer length: 512 len:2 - 82 5

On windows environment I tested, the header and data when sending to device does not need to be separated (i.e can but them to one array and send that) ,but I guess there is still some separation between data packages that the Loupedeck expects.

What is the "rx token: 1FE8100 transfer length: 512 len:2 - 82 5 " that comes from the USBHost_t36.h debug print (8255103FF comes from my printout). How does it know the rx: 82 05 is a data package? the rx: 05 01 00 03 FF follows right after. So maybe it detects some handshake? (or just the the length: 512?) and that handshake is not applied to Tx?

note1: This device needs USBSerial_BigBuffer, that is also obvious looking above the length: 512

note2: for tx there is the above mentioned problem sending 512 "*** Empty ***" 511 is fine. And I am not sure if the flush works.
 
Ok, I've just re-read all 57 messages of this thread.

Just to recap and make sure I understand (and to help bring anyone else up to speed without reading all 57 messages), it seems you bought a product called Loupedeck which is a USB device that works with software on Windows and MacOS. I not head of this product, but a quick Google search turned up this website which seems to have at least 3 different products, called "Loupedeck+", "Loupedeck Live" and "Loupedeck CT".

https://loupedeck.com/us/products/

Over the last 50 messages, this thread has become a confusing combination of many unknowns. But it seems they all fall into 2 categories:

1: What communication does Loupedeck expect with the supplied Windows / MacOS software?

2: Is any of the trouble when trying to communicate with Loupedeck plugged into Teensy's USB host port due to bugs or issues within the USBHost_t36 library.

While I can not rule out #2, it seems pretty unlikely. All signs suggest Loupedeck is using ordinary CDC ACM serial protocol, which works fine. You may be hitting a previously unknown bug, but I do not believe that is the likely explanation. I'm pretty sure the problem is in the first category.

The USBHost_t36 debug messages show a lot of information about the inner working of the library. It's probably far too much info. Those messages are primarily meant for developers working on the library itself. I know you're just trying to solve a problem and all the info gives you a lot of things to question. But focusing so much of your effort on the very low-level USB protocol details like zero-length packets and high speed ping protocol is very likely counterproductive.

My main suggestion at this point is to focus mostly on #1, and specifically to create software on the PC which communicates with the Loupedeck product you have. Looks like you've already done some of this by using Labview. I personally don't know much about Labview, but the few times I've seen it used the general approach was very graphical and probably not the best way to focus on the exact bytes transmitted and received. Maybe writing in C or Python might be better? Those are certainly easier to show us on this forum. But if Labview is familiar and you really can clearly control the exact bytes it transmits and see all the bytes it receives from Loupedeck, then it should be fine.

I see in msg #52 you mentioned some communication success between Labview and Loupedeck "got just button colour controlled, so the control works".

Maybe if you can show us the bytes Labview transmitted and received, and then the code you're attempting on Teensy to do the same, perhaps we'll be able to see a difference? But remember, I know almost nothing of Labview. I also don't use Wireshark. So how to show the Labview program and show the exact bytes transmitted and received, I'm not sure. Often times on this forum people show us small code fragments or pseudo-code which is meant to be easier to read, but over and over that ends up being a bad way to get help because small but important details are almost always lost in the translation.

Anyway, please, try to show us what communication you were able to write on the PC (or Mac) which successfully communicated with Loupedeck, and the full program you ran on Teensy 4.1 which tried to do the same thing but didn't give the same result. Maybe we'll be able to see the problem if the info is presented in a clear way.
 
When I started with the Loupedeck Live, I got the first step pretty soon, i.e initialising it and receiving button presses.

Then I got stuck. What ever I did I did get the binary messages trough, but the device goes to sleep, so sorry yes, on those 50 messages there is too many things going on, me just simply not knowing what is failing.

The current status as said, I am pretty confident I am sending the same bytes from Labview and Teensy. With Labview I get response and action on device (button changes colour) and response. With Teensy, no button colour change, no response, devise goes to sleep.

I will make couple of videos, the code has been posted above, but here again, the simplified Teensy code that I am now testing with. (this one sends only the first initialisation and after gets button presses. Uncomment the sending section, and will send and go to sleep (on this section I have tried many things).

Code:
#include <USBHost_t36.h>
#define USBBAUD 115200 //2096000
uint32_t baud = USBBAUD;
uint32_t format = USBHOST_SERIAL_8N1;
uint32_t Timer;


char      WS_UPGRADE_HEADER[] = "GET /index.html\r\n"
                                "HTTP/1.1\r\n"
                                "Connection: Upgrade\r\n"
                                "Upgrade: websocket\r\n"
                                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";
                                

bool      FistByteReceived = false;


uint8_t SHORT_HEADER[]={0x82, 0x80, 0x00, 0x00, 0x00, 0x00};


USBHost USB_SER;
USBHub hub1(USB_SER);
USBSerial_BigBuffer USB_serial(USB_SER, 1); // Handles anything up to 512 bytes



USBDriver *drivers[] = {&hub1, &USB_serial};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "USB_serial" };
bool driver_active[CNT_DEVICES] = {false, false};

void setup()
{
  while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB Host Testing - Serial");
  USB_SER.begin();
  delay(1000);
}


void loop()
{
  
  if((micros() - Timer)>1000){
      Timer=micros();
      USB_SER.Task();
      // Print out information about different devices.
      for (uint8_t i = 0; i < CNT_DEVICES; i++) {
        
        if (*drivers[i] != driver_active[i]) {
          Serial.println("///////////////////////////////////////////////////////");
          if (driver_active[i]) {
            Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
            driver_active[i] = false;
            FistByteReceived = false;
    
          } else {
            Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
                
            driver_active[i] = true;
    
            const uint8_t *psz = drivers[i]->manufacturer();
            if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
            psz = drivers[i]->product();
            if (psz && *psz) Serial.printf("  product: %s\n", psz);
            psz = drivers[i]->serialNumber();
            if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
    
            // If this is a new Serial device.
            if (drivers[i] == &USB_serial) {  USB_serial.begin(baud, format ); Serial.println("USB_serial started");}
           
          }
        }
      }
  }


if (FistByteReceived == false){
      //this goes out ok
      Serial.println("\r\nRequest1 WS \r\n");
      Serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));
      USB_serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));
      delay(200);
      //we get expected response
      Serial.println("\r\nResponse1 \r\n");
      while (USB_serial.available()) {Serial.write(USB_serial.read());  FistByteReceived = true; }

  /* 
      Serial.println("TEST \r\n");

   // Serial.println(HW_USBPHY_TX_D_CAL);
      uint8_t   COMMAND[] = { 0x07, 0x02, 0x0f, 0x0a, 0xff, 0x00, 0x00 };//button 10 red
      
      
      
      SHORT_HEADER[1] = 0x80+sizeof(COMMAND );
       
       Serial.println("START \r\n");


       for (uint16_t i = 0; i < sizeof(SHORT_HEADER); i++) {USB_serial.write((uint8_t)SHORT_HEADER[i]); }
       delay(10);
       for (uint16_t i = 0; i < sizeof(COMMAND     ); i++) {USB_serial.write((uint8_t)COMMAND     [i]); }
    
       delay(10);
    

      // USB_serial.write((uint8_t)SHORT_HEADER , sizeof(SHORT_HEADER ));  
      // USB_serial.write((uint8_t)COMMAND , sizeof(COMMAND ));
       //for (uint16_t i = 0; i < 512; i++) {USB_serial.write((uint8_t)0x00); }
       delay(10);
       
       
       while (USB_serial.available()) {Serial.print("RECEIVED2: ");Serial.print(USB_serial.read(), HEX);   }
       delay(100);
       //just making sure we did not miss any incoming data, and nothing received
       while (USB_serial.available()) {Serial.print("RECEIVED3: ");Serial.print(USB_serial.read(), HEX);   }
       Serial.println("DONE \r\n");
*/
      }





      while (USB_serial.available()) {Serial.print(USB_serial.read(), HEX);  FistByteReceived = true; }


}

This is the also above linked PDF file of Wireshark screenshots, The Loupedeck Live communications with its own SW https://drive.google.com/file/d/1i96Vabbc5HP7idVxVJb53NmdOWDSdI2n/view

On this directory is also the complete Wireshark recording (requires Wireshark to view) , and I will put there also everything else related https://drive.google.com/drive/folders/1WBTS8CHe4cD77GUWxybnNvd6P5jhmq8o?usp=share_link
 
Looks like the very first time loop() runs, it will send the request.

There seems to be nothing in your program to assure that USB_serial.begin(baud, format ); happens before you use Serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));

Perhaps your program is transmitting the message before Loupedeck is even enumerated? Your program has lots of Serial.print() but I can't see your screen and you didn't show what appeared in the Arduino Serial Monitor, so I can't see the sequence of events.


Edit: before copying a program here, in Arduino press CTRL-T to format it nicely. This makes reading your program much easier for everyone else. If you don't like the result, just save before pressing CTRL-T and then reload the saved file after showing up the nicely formatted version.
 
You can see the output (and code) on post 53.

Should this not make sure it gets the baud and format, when device is found? but where would be good to put it.

// If this is a new Serial device.
if (drivers == &USB_serial) { USB_serial.begin(baud, format ); Serial.println("USB_serial started");}

It is a question what should the baud and format be. As I understand from this
control callback (serial) 6
CDCACM setup: 00 C2 01 00 00 00 08

#define USBBAUD 2096000
uint32_t baud = USBBAUD;
uint32_t format = USBHOST_SERIAL_8N1;

But have tried many other things also. No effect.

To be clear, the


USB_serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));

works and provides response

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

as expected.

It is the binary data sending after that fails. (the commented section on last posted code)
 
I did not read that commented part, because it was commented out.

Can you show the correct binary data Windows sends? (here on this forum, not google drive)
 
Can you also show the program you created on Windows which works?

In other words, there may be a "simple" error or misunderstanding you've overlooked that makes your program do something differently than the known-good program on Windows. We see this often... it's very easy to overlook an error. If you show us your not-working Teensy program and working Windows program, and a (small) screenshot of the correct data capture, maybe we can notice what is wrong.

The smaller and simpler (but complete) the programs are, and the simpler the data capture is to view and understand, the better chance one of us might be able to spot what is different on the Teensy code versus the Windows code. Let us try to help you, by showing us both in the clearest, simplest, easiest to read way you can. I want to help, and others here probably do too. But we can't see this stuff easily and this thread has turned into dozens of message diving much too deep into USB low level stuff. Odds are this may be just a "simple" mistake, but nobody can see it if you don't show us the good and bad code and communication capture clearly.
 
It really would be best seeing the Wireshark file from the google drive , there is a lot of information but Wireshark makes good presentation of it. The next best this was making screenshots of it and that is as PDF file on the drive. It would be many big screenshots to post n here. It is not really presentable as text. I will now make video of the Labview and post soon.
 
This form allows images. Click the "insert image" button in the editor toolbar. Images need to be under 1280 pixels, so make sure to trim away excess so only the important info is shown.

Remember, everyone here (myself included) is just casually reading these messages. Make it easy for us to see. We can help more when we're able to see.
 
Here is the controlling the Loupedeck Live with LabView.

https://vimeo.com/771594484/888c0f9d2f

Yes, it is not as straight forward to tell what the LabView Data formats are, but I have used it many times to communicate with teensy and what is received at Teensy end is just the bytes sent when printing directly the incoming data.

I forgot to show the response from the button colour command. Here it is the last 4 bytes.

0x82 is the start byte telling it is binary data
0x03 is the data length given in header
0x03 is the data length given at start of the data
0x02, 0x0f is the response data (do not know what that means

Note on this display the format has been selected so that it shows ASCII when letter and otherwise hexadecimal, not really familiar how it works, but with it got both displayed.

View attachment 29747

On this one the same data (different run, startup data sent once), but display format selected HEX

View attachment 29748

Note: with Teensy, the first initialisation works the same, but sending the same command, same header formatted the same the best I can say, and no response, button colour does not come on, Loupedeck live goes to sleep.
 
Last edited:
There is just too much on those pictures to present on that resolution. The pdf https://drive.google.com/file/d/1i96Vabbc5HP7idVxVJb53NmdOWDSdI2n/view?usp=share_link is the easiest way to read it for sure and best is to get the Wireshark and use native format, the file in here https://drive.google.com/drive/folders/1WBTS8CHe4cD77GUWxybnNvd6P5jhmq8o?usp=share_link

The Wireshark seem to work very well on Windows PC to get all USB communication (so I think after short use, and was not difficult to use as I thought)

I will now connect two teensy together via HW serial and do just pass trough to get the bytes on terminal that LabView and Teensy USB sends, so after there should be no doubts what was sent (as bytes, but the below USB serial we will not done with this, Wireshark could show communication between LabView and Loupedeck, but if no one is looking those files no point.).

This form allows images. Click the "insert image" button in the editor toolbar. Images need to be under 1280 pixels, so make sure to trim away excess so only the important info is shown.

Remember, everyone here (myself included) is just casually reading these messages. Make it easy for us to see. We can help more when we're able to see.
 
Ok, so I made this connection,


IMG_2520.jpg


two Teensy 3.2 Serial 1 connected together to act as USB to USB bridge

Both have this code, but commented differently, see the comments.

Code:
uint8_t BYTE;
uint8_t counter;


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
}


void loop()
{

  if (Serial.available()  > 0) Serial1.write(Serial.read());

  //use on the one connected to Labview or Teensy USB Host
  //if (Serial1.available() > 0) Serial.write(Serial1.read());

  //use on the terminal connection
  if (Serial1.available() > 0) {
    
    BYTE = Serial1.read();
    
    if (BYTE < 16 ) Serial.print("0x0"); else Serial.print("0x");
    
    Serial.print(BYTE, HEX); 
    
    counter++;
    
    if(counter == 16) {Serial.println(); counter = 0;} else {Serial.print(", ");}
  }


}


And here are the printouts


Code:
With Teensy USB Host

0x47, 0x45, 0x54, 0x20, 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0x0D
0x0A, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x31, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x6E, 0x65
0x63, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0D, 0x0A
0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3A, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B
0x65, 0x74, 0x0D, 0x0A, 0x53, 0x65, 0x63, 0x2D, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65
0x74, 0x2D, 0x4B, 0x65, 0x79, 0x3A, 0x20, 0x64, 0x47, 0x68, 0x6C, 0x49, 0x48, 0x4E, 0x68, 0x62
0x58, 0x42, 0x73, 0x5A, 0x53, 0x42, 0x75, 0x62, 0x32, 0x35, 0x6A, 0x5A, 0x51, 0x3D, 0x3D, 0x0D
0x0A, 0x0D, 0x0A, 0x00, 0x82, 0x87, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00
0x00,


With LabView

0x47, 0x45, 0x54, 0x20, 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, 0x0D, 0x0A
0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x31, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63
0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0D, 0x0A, 0x53
0x65, 0x63, 0x2D, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x2D, 0x4B, 0x65, 0x79
0x3A, 0x20, 0x64, 0x47, 0x68, 0x6C, 0x49, 0x48, 0x4E, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5A, 0x53
0x42, 0x75, 0x62, 0x32, 0x35, 0x6A, 0x5A, 0x51, 0x3D, 0x3D, 0x0D, 0x0A, 0x0D, 0x0A, 0x82, 0x87
0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00, 0x00,

First is the initialisation

char WS_UPGRADE_HEADER[] = "GET /index.html\r\n"
"HTTP/1.1\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";

They look slightly different because the line endings, but both work and provide wanted response.

The code that fails when sent to Loupedeck with Teensy USB Host but not with Labview is the important part

0x82, 0x87, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00, 0x00
0x82, 0x87, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00, 0x00

Just copied it from both printouts here and it is exactly the same.

I will still fix the WS_UPGRADE_HEADER to be exactly same as on LabView, but do not see how that would make any difference, we get response to it on both cases. but could be wrong, there is a suspicious 0x00 just before the command, that should not be there. Also noted that the LabView that works, is missing the "Upgrade: websocket\r\n" by accident, will test the same on the Teensy code.
 
Sorry, I have been MIA here for a couple of days, been investigating some other things (Bluetooth...) And only so many things I can keep straight at time. (Usually, about 0 ;) )

As Paul mentioned, I have doubts that you need things like 0 length packets... But I have been wrong before.
You could probably hack a version of flush() that would do it.
That is add a new method like flushEmpty() into the class, and then more or less do the same as flush except that, it would try to send a logically empty packet.
The flush code probably needs some minor fixes in it, as you mentioned it does not wait for a flush to complete before continuing.

Need to experiment with it again. to do the transmit directly, was just trying to avoid a race condition earlier. And the main reason I had the flush was to reduce latency, as usually this was done when I needed to then wait for a response

Will try to look again soon.
 
Code:
With Teensy USB Host

0x47, 0x45, 0x54, 0x20, 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, [COLOR="#FF0000"]0x6C, [/COLOR]0x0D, 0x0A
0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x31, 0x0D, 0x0A
0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0D, 0x0A
[COLOR="#FF0000"]0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3A, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x0D, 0x0A[/COLOR]
0x53, 0x65, 0x63, 0x2D, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x2D, 0x4B, 0x65, 0x79, 0x3A, 0x20, 0x64, 0x47, 0x68, 0x6C, 0x49, 0x48, 0x4E, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5A, 0x53, 0x42, 0x75, 0x62, 0x32, 0x35, 0x6A, 0x5A, 0x51, 0x3D, 0x3D, 0x0D, 0x0A
0x0D, 0x0A
[COLOR="#FF0000"]0x00, [/COLOR]0x82, 0x87, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00, 0x00,

With LabView

0x47, 0x45, 0x54, 0x20, 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, 0x0D, 0x0A
0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x31, 0x0D, 0x0A
0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0D, 0x0A
0x53, 0x65, 0x63, 0x2D, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x2D, 0x4B, 0x65, 0x79, 0x3A, 0x20, 0x64, 0x47, 0x68, 0x6C, 0x49, 0x48, 0x4E, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5A, 0x53, 0x42, 0x75, 0x62, 0x32, 0x35, 0x6A, 0x5A, 0x51, 0x3D, 0x3D, 0x0D, 0x0A
0x0D, 0x0A
0x82, 0x87, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x0F, 0x0A, 0xFF, 0x00, 0x00
I don't know what is producing the responses above, but I have highlighted the extra bits in the Teensy Host.

Hopefully this might help someone help you or understand what is happening.
 
Thanks BriComp, Yes I noted those, There was mistakes on the LabView text that works? the 0x6C was a missing l, the longer section missing "Upgrade: websocket\r\n"

The strange thing is that both codes get the same response to this, so Loupedeck is likely just looking few keywords on that.

However the 0x00 I do not know where that is coming. Looking in to it now.
 
There is something funny going on with

USB_serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER));

The WS_UPGRADE_HEADER it should send is

Code:
char      WS_UPGRADE_HEADER[] = "GET /index.htm\r\n"
                                "HTTP/1.1\r\n"
                                "Connection: Upgrade\r\n"
                                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";


The data I get is

Code:
0x47, 0x45, 0x54, 0x20, 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, 0x0D, 0x0A
0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x31, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63
0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0D, 0x0A, 0x53
0x65, 0x63, 0x2D, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x2D, 0x4B, 0x65, 0x79
0x3A, 0x20, 0x64, 0x47, 0x68, 0x6C, 0x49, 0x48, 0x4E, 0x68, 0x62, 0x58, 0x42, 0x73, 0x5A, 0x53
0x42, 0x75, 0x62, 0x32, 0x35, 0x6A, 0x5A, 0x51, 0x3D, 0x3D, 0x0D, 0x0A, 0x0D, 0x0A, 0x00

Where is that last 0x00 coming from?

If I change the USB_serial.write(WS_UPGRADE_HEADER, sizeof(WS_UPGRADE_HEADER)-1); i.e reduce the size by 1 the last zero is gone. But where is it coming from, why it gets the size wrong?
 
Some other things to try to look at,

a) Is there any pacing of stuff from the host to it. For example, the Init code, are there pauses or the like, for example one command might be a reset, and the code may know that the reset will take some time, so it might delay some X Micro or Milli seconds before it tries to send any additional stuff...

b) Does it play around at all with Serial baud rate. As has been mentioned before this means nothing to the USB communications, BUT:
If what USB device you are talking to, is something like a USB to Serial Adapter chip, like an FTDI or the like. Often times it has an internal UART, that it uses to talk rest of the device.
For example, Arduino UNO, has a secondary chip (used to be something like an FTDI, now another AVR chip). So these devices use the Baud Rate request, stuff to then configure their own internal UART to match
what the other device is expecting. ...

Note this may also not be a static thing. For example, maybe the device always starts at lets say 9600 baud, which the rest of the device starts up as. But maybe has an init command, saying, please change
the baud rate of the internal UART to another speed like 115200 (or faster). Then when the command is received and that device switches to the new speed, your expected then to change the USB baud rate to match...
 
Thanks for the baud rate clarification, I have always wondered that. I have tried many baud rates. also on the LabView code that works and not noted any difference. Also I have tried many different formats, now using format = USBHOST_SERIAL_8N1;, but actually do not know what LabView uses.
 
Back
Top