mborgerson
Well-known member
I'm talking about the fact that the USB Serial Host driver does not allow one Teensy to talk to another.
The Serial Host driver will only communicate with devices that are specified in the serial.cpp driver in the USBHost_t36 library. The relevant code starts at line 45:
Until I added it, there was no mapping for the T4.1--or any other Teensy, for that matter.
So I added one for the T4.1 before the FTDI mappings:
{0x16C0, 0x0483, USBSerialBase::TEENSY41, 1}, // interface level Teensy 4.1.
Not so fast--the compiler notified me that TEENSY41 was not defined. So I went to the USBSerialBase section of USBHost_t36 and added TEENSY41 to the enum specifying serial device types and added TEENSY41 at line #1617:
// Added TEENSY41 to enum typedef MJB 12/12/2023
typedef enum { UNKNOWN = 0, CDCACM, FTDI, PL2303, CH341, CP210X, TEENSY41} sertype_t;
Were the Teensys ready to talk to each other? I hoped so. I wrote two simple programs:
1: A T4.1 host program that implements a host serial port and listens for input and prints what it gets.
2: A T4.1 device program that opens its Serial port and sends a short message once per second.
These two programs are included in the following code block:
SerialSender was tested by observing its output with a terminal program. It sends a message about once per second:
Teensy 4.1 message #0
Teensy 4.1 message #1
Teensy 4.1 message #2
etc.
It was time to connect the two family members. I setup the host serial listener to produce debug output, connected the T41. with the sender to the USB Host port and started the listener program. The result was a bit like having my wife tell me a story about two people I don't know attending a master gardeners' function that I've never experienced---while I was watching the last minutes of a close Seattle Seahawks game. After about 30 seconds, nothing was received.
To quote the prison gang captain in Cool Hand Luke: "What we've got here is failure to communicate. "
The output from the listener program is in the following code block. I've added some comments and deleted some of the longer hexadecimal data dumps.
Does anyone have any idea why the host program receives no data after the first transfer?
Backstory:
This test was prompted by some problems I am having with the connection between a T4.1 as a serial host and a FLIR Boson camera. The Boson implements a USB Serial interface for control, status and data transfers. The interface is a straight implementation of a UART SLIP link with checksums. I can send commands and the camera responds (I can hear the shutter click when I request a background calibration operation.) However, when I send a request that requires a return data packet, the packet is always missing the first two bytes and the bad checksum results in a BAD PACKET error. The USBHost driver for that test is greatly modified from the USBHost_t36 code. When adapting existing code I usually remove parts that I think have no function in the target library. In the case of USBSerial Host code, I don't need code for a half dozen serial converters that I will never use while developing the Boson driver. I also don't need all the code to handle serial port handshaking as that is superfluous with USB to USB bulk transfers. I also do my best to reduce the sea of #IFDEFS to a small puddle. I appreciate the utility of one library to control them all and one folder to bind them all, but there's a major difference between USING a library and UNDERSTANDING a library. USBHost_t36.h is 2830 lines of source code. Serial.cpp is 1476 lines. My redacted USBHost_MJB is about half the size of the original and BosonSerial source is about 900 lines. Even so, I'm developing a callous on my index finger from the scroll wheel on my mouse. I need to start using the code-folding capabilities of the IDE more often!
The Serial Host driver will only communicate with devices that are specified in the serial.cpp driver in the USBHost_t36 library. The relevant code starts at line 45:
Code:
USBSerialBase::product_vendor_mapping_t USBSerialBase::pid_vid_mapping[] = {
// FTDI mappings.
{0x0403, 0x6001, USBSerialBase::FTDI, 0},
{0x0403, 0x8088, USBSerialBase::FTDI, 1}, // 2 devices try to claim at interface level
{0x0403, 0x6010, USBSerialBase::FTDI, 1}, // Also Dual Serial, so claim at interface level
etc., etc.
Until I added it, there was no mapping for the T4.1--or any other Teensy, for that matter.
So I added one for the T4.1 before the FTDI mappings:
{0x16C0, 0x0483, USBSerialBase::TEENSY41, 1}, // interface level Teensy 4.1.
Not so fast--the compiler notified me that TEENSY41 was not defined. So I went to the USBSerialBase section of USBHost_t36 and added TEENSY41 to the enum specifying serial device types and added TEENSY41 at line #1617:
// Added TEENSY41 to enum typedef MJB 12/12/2023
typedef enum { UNKNOWN = 0, CDCACM, FTDI, PL2303, CH341, CP210X, TEENSY41} sertype_t;
Were the Teensys ready to talk to each other? I hoped so. I wrote two simple programs:
1: A T4.1 host program that implements a host serial port and listens for input and prints what it gets.
2: A T4.1 device program that opens its Serial port and sends a short message once per second.
These two programs are included in the following code block:
Code:
// HostSerialListener
// Establish host comms with connected T4.1
// and listen for input.
// MJB 12/12/2023
#include "USBHost_t36.h"
const int pinLED = 13;
#define LEDON digitalWriteFast(pinLED, HIGH);
#define LEDOFF digitalWriteFast(pinLED, LOW);
#define LEDTOGGLE digitalToggleFast(pinLED);
USBHost myusb;
USBHub hub1(myusb);
USBSerial_BigBuffer myHSerial(myusb); // Teensy 4.1 has 512-byte max transfer
void setup() {
elapsedMillis serialtimer;
// Wait for Serial connection--but only for 3 seconds
pinMode(pinLED, OUTPUT);
LEDON
Serial.begin(9600);
while (!Serial && (serialtimer < 3000)) {
delay(50);
}
LEDOFF
Serial.println("\n\n Teensy USB Host Serial Listener test.");
delay(1000);
// Start USBHost_t36, HUB(s) and USB devices.
myusb.begin();
myusb.Task();
Serial.printf("myusb at: %p\n", &myusb);
Serial.printf("myHSerial at: %p\n", &myHSerial);
myHSerial.begin(9600); // start the USB Host serial
delay(100);
Serial.println("Proceeding to Listener loop()");
delay(100);
}
void loop() {
char ch;
myusb.Task();
LEDON
if (myHSerial.available()) {
ch = myHSerial.read();
Serial.print(ch);
delay(5); // longer and brighter flash when receiving
}
delay(2);
LEDOFF
delay(5);
myusb.Task();
}
// SerialSend program
// Simple sketch to send data to serial port once per second.
// Used to test whether T4.1 USB Host receives data
const int pinLED = 13;
#define LEDON digitalWriteFast(pinLED, HIGH);
#define LEDOFF digitalWriteFast(pinLED, LOW);
#define LEDTOGGLE digitalToggleFast(pinLED);
void setup() {
elapsedMillis serialtimer;
pinMode(pinLED, OUTPUT);
LEDON
LEDON
Serial.begin(9600);
while (!Serial && (serialtimer < 3000)) {
delay(50);
}
delay(1000); // visual indication that program is running
LEDOFF
}
void loop() {
static uint32_t msgnum = 0;
LEDON
Serial.print("Teensy 4.1 message #");
Serial.println(msgnum);
msgnum++;
delay(200); // short flash once per second
LEDOFF
delay(800);
}
SerialSender was tested by observing its output with a terminal program. It sends a message about once per second:
Teensy 4.1 message #0
Teensy 4.1 message #1
Teensy 4.1 message #2
etc.
It was time to connect the two family members. I setup the host serial listener to produce debug output, connected the T41. with the sender to the USB Host port and started the listener program. The result was a bit like having my wife tell me a story about two people I don't know attending a master gardeners' function that I've never experienced---while I was watching the last minutes of a close Seattle Seahawks game. After about 30 seconds, nothing was received.
To quote the prison gang captain in Cool Hand Luke: "What we've got here is failure to communicate. "
The output from the listener program is in the following code block. I've added some comments and deleted some of the longer hexadecimal data dumps.
Code:
// Output from listener test with Host debug enabled
// Collected 12/12/2023
// comments added after //
Teensy USB Host Serial Listener test.
USB2 PLL running
reset waited 6
USBHS_ASYNCLISTADDR = 0
USBHS_PERIODICLISTBASE = 20004000
periodictable = 20004000
myusb at: 0x20006760
myHSerial at: 0x200045e0
control callback (serial) 6
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 C0 16 83 04 80 02 01 02 03 01
VendorID = 16C0, ProductID = 0483, Version = 0280 // Note T4.1 vender and product ID
Class/Subclass/Protocol = 239 / 2 / 1
Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: Teensyduino
enumeration:
Product: USB Serial
enumeration:
Serial Number: 12229190
enumeration:
Config data length = 98
enumeration:
Configuration Descriptor:
09 02 62 00 03 01 00 C0 32
NumInterfaces = 3
ConfigurationValue = 1
08 0B 00 02 02 02 01 00
Interface Association = 0 through 1
Class / Subclass / Protocol = 2 / 2 / 0 // Serial device class and subclass
09 04 00 00 01 02 02 01 00
Interface = 0
Number of endpoints = 1
Class/Subclass/Protocol = 2 / 2 / 1
05 24 00 10 01
05 24 01 01 01
04 24 02 06
05 24 06 00 01
07 05 82 03 10 00 05
Endpoint = 2 IN // Im not sure how the interrupt end point is used
Type = Interrupt
Max Size = 16
Polling Interval = 5
09 04 01 00 02 0A 00 00 00
Interface = 1 // Second interface (after control at 0)
Number of endpoints = 2
Class/Subclass/Protocol = 10 / 0 / 0 CDC Data interface
07 05 03 02 00 02 00
Endpoint = 3 OUT
Type = Bulk
Max Size = 512 // 512-byte bulk endpoint, as expected
Polling Interval = 0
07 05 84 02 00 02 00
Endpoint = 4 IN
Type = Bulk
Max Size = 512 // 512-byte bulk endpoint, as expected
Polling Interval = 0
09 04 02 00 02 FF 6A C7 00
Interface = 2 // Third interface
Number of endpoints = 2 //
Class/Subclass/Protocol = 255 / 106 / 199 // Vendor Specific Class (for Teensy loader?)
07 05 81 02 00 02 01
Endpoint = 1 IN
Type = Bulk
Max Size = 512
Polling Interval = 1
07 05 01 02 00 02 01
Endpoint = 1 OUT
Type = Bulk
Max Size = 512
Polling Interval = 1
enumeration:
USBHub memory usage = 960
USBHub claim_device this=20004220
USBSerial(512)claim this=200045E0
vid=16C0, pid=483, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1 // Miscellaneous
08 0B 00 02 02 02 01 00 09 04 00 00 01 02 02 01 00 05
//hex print deleted for brevity
Serial device wants to map at interface level
Descriptor 11 = IAD
Descriptor 4 = INTERFACE
USBSerial(512)claim this=200045E0
vid=16C0, pid=483, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1 // Miscellaneous
09 04 00 00 01 02 02 01 00 05 24 00 10 01 05
//hex print deleted for brevity
len = 81
Descriptor 36 = ???
Descriptor 36 = ???
Descriptor 36 = ???
Descriptor 36 = ???
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
USBSerial(512)claim this=200045E0
vid=16C0, pid=483, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1 // miscellaneous
09 04 01 00 02 0A 00 00 00 07 05 03 02 00 02 00 07 05 84 02 00 02 00 09 04 02 00 02 FF 6A C7 00 07 05 81 02 00 02 09 04 01 00 02 0A 00 00 00 07 05 03 02 00 02 00 07 05 84 02 00 02 00 09 04 02 00 02 FF 6A C7 00 07 05 81 02 00 02 01 07
5 01 02 00 02 01
len = 46
USBSerial, rxep=4(512), txep=3(512)
rx buffer size:1024
tx buffer size:1024
new_Pipe
new_Pipe
Descriptor 5 = ENDPOINT
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
USBSerial(512)claim this=200045E0
vid=16C0, pid=483, bDeviceClass = 239, bDeviceSubClass = 2, bDeviceProtocol = 1
09 04 02 00 02 FF 6A C7 00 07 05 81 02 00 02 01 07 05 01 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
Descriptor 5 = ENDPOINT
Descriptor 5 = ENDPOINT
// Here's something interesting: Before the program gets to the loop() function,
// the USB Serial Host receives a data packet from the SerialSend program on the
// connected T4.1.
rx token: 81E98100 transfer length: 512 len:23 - 54 65
rx: 54 65 65 6E 73 79 20 34 2E 31 20 6D 65 73 73 61 67 65 20 23 30 0D 0A
// Translation: "Teensy 4.1 message #0\n\r" This the first message from the sender
USBSerialBase::begin timeout6
Proceeding to Listener loop()
// Listener loop receives no more data, even though T4.1 device is sending once per second.
Does anyone have any idea why the host program receives no data after the first transfer?
Backstory:
This test was prompted by some problems I am having with the connection between a T4.1 as a serial host and a FLIR Boson camera. The Boson implements a USB Serial interface for control, status and data transfers. The interface is a straight implementation of a UART SLIP link with checksums. I can send commands and the camera responds (I can hear the shutter click when I request a background calibration operation.) However, when I send a request that requires a return data packet, the packet is always missing the first two bytes and the bad checksum results in a BAD PACKET error. The USBHost driver for that test is greatly modified from the USBHost_t36 code. When adapting existing code I usually remove parts that I think have no function in the target library. In the case of USBSerial Host code, I don't need code for a half dozen serial converters that I will never use while developing the Boson driver. I also don't need all the code to handle serial port handshaking as that is superfluous with USB to USB bulk transfers. I also do my best to reduce the sea of #IFDEFS to a small puddle. I appreciate the utility of one library to control them all and one folder to bind them all, but there's a major difference between USING a library and UNDERSTANDING a library. USBHost_t36.h is 2830 lines of source code. Serial.cpp is 1476 lines. My redacted USBHost_MJB is about half the size of the original and BosonSerial source is about 900 lines. Even so, I'm developing a callous on my index finger from the scroll wheel on my mouse. I need to start using the code-folding capabilities of the IDE more often!