I am trying to add an unsupported USB device to the USBhost_t36 library using the Teensy 4.1. Someone already wrote code for this USB device for the ESP32, which can be found here: https://github.com/Builty/TonexOneController/tree/main/source/main
My problem is that I do not seem to receive any data from the USB device. TonexOne::rx_data(...) is never executed. Sending data works fine. Am I missing something obvious? Do I need to actively poll for receiving data? I cannot find any examples of this in the library.
If anybody familiar with the USBhost_t36 library could help me out, i would be most grateful.
Here is my code:
TonexOne.ino:
T1_USB.h:
T1_USB.c:
My problem is that I do not seem to receive any data from the USB device. TonexOne::rx_data(...) is never executed. Sending data works fine. Am I missing something obvious? Do I need to actively poll for receiving data? I cannot find any examples of this in the library.
If anybody familiar with the USBhost_t36 library could help me out, i would be most grateful.
Here is my code:
TonexOne.ino:
Code:
#include "T1_USB.h"
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);
TonexOne my_T1(myusb);
void setup() {
Serial.begin(115200);
myusb.begin();
task_delay(1000);
Serial.println("Send hello");
my_T1.Send_hello();
task_delay(1000);
Serial.println("Request state");
my_T1.Request_state();
task_delay(100);
Serial.println("Request data part 1");
my_T1.Request_data_part1(0);
Serial.println("Request data part 2");
//my_T1.Request_data_part2(0);
}
void loop() {
myusb.Task();
for (uint8_t p = 0; p < 20; p++) {
task_delay(500);
myusb.Task();
Serial.println("*************Selecting patch" + String(p));
my_T1.SetPatch(0, 1, p, 2);
}
}
void task_delay(uint16_t time) {
uint16_t end_time = millis() + time;
do {
myusb.Task();
} while (millis() < end_time);
}
T1_USB.h:
Code:
#include <USBHost_t36.h>
class TonexOne: public USBDriver {
public:
TonexOne(USBHost &host) { init(); }
void Send_hello();
void Request_state();
void Request_data_part1(uint8_t patch);
void Request_data_part2(uint8_t patch);
void SetPatch(uint8_t number_slot1, uint8_t number_slot2, uint8_t number_slot3, uint8_t active_slot);
protected:
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len);
virtual void disconnect();
//virtual void timer_event(USBDriverTimer *whichTimer);
private:
static void rx_callback(const Transfer_t *transfer);
static void tx_callback(const Transfer_t *transfer);
void rx_data(const Transfer_t *transfer);
void tx_data(const Transfer_t *transfer);
void init();
size_t write(const void *data, const size_t size);
//int read(void *data, const size_t size);
void transmit();
uint16_t addByteWithStuffing(uint8_t* output, uint8_t byte);
uint16_t calculateCRC(uint8_t* data, uint16_t length);
uint16_t addFraming(uint8_t* input, uint16_t inlength, uint8_t* output);
private:
Pipe_t mypipes[2] __attribute__ ((aligned(32)));
Transfer_t mytransfers[3] __attribute__ ((aligned(32)));
strbuf_t mystring_bufs[1];
//USBDriverTimer txtimer;
USBDriverTimer updatetimer;
Pipe_t *rxpipe;
Pipe_t *txpipe;
bool first_update;
uint8_t txbuffer[11240];
uint8_t rxpacket[11240];
volatile uint16_t txhead;
volatile uint16_t txtail;
volatile bool txready;
volatile uint8_t rxlen;
volatile bool do_polling;
};
T1_USB.c:
Code:
#include <Arduino.h>
#include "USBHost_t36.h" // Read this header first for key info
#define TONEXONE_VID 0x1963
#define TONEXONE_PID 0x00D1
#define print USBHost::print_
#define println USBHost::println_
#define ENABLE_SERIALPRINTF 1
#if ENABLE_SERIALPRINTF
#undef printf
#define printf(...) Serial.printf(__VA_ARGS__); Serial.write("\r\n")
#else
#undef printf
#define printf(...)
#endif
#include "T1_USB.h"
void TonexOne::init()
{
contribute_Pipes(mypipes, sizeof(mypipes) / sizeof(Pipe_t));
contribute_Transfers(mytransfers, sizeof(mytransfers) / sizeof(Transfer_t));
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs) / sizeof(strbuf_t));
driver_ready_for_device(this);
}
bool TonexOne::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
{
if (type != 1) return false;
println("TonexOne claim this=", (uint32_t)this, HEX);
if (dev->idVendor != TONEXONE_VID) return false;
if (dev->idProduct != TONEXONE_PID) return false;
println("found TonexOne, pid=", dev->idProduct, HEX);
rxpipe = txpipe = NULL;
const uint8_t *p = descriptors;
const uint8_t *end = p + len;
int descriptorLength = p[0];
int descriptorType = p[1];
if (descriptorLength < 9 || descriptorType != 4) return false;
p += descriptorLength;
while (p < end) {
descriptorLength = p[0];
if (p + descriptorLength > end) return false; // reject if beyond end of data
descriptorType = p[1];
if (descriptorType == 5) { // 5 = endpoint
uint8_t epAddr = p[2];
uint8_t epType = p[3] & 0x03;
uint16_t epSize = p[4] | (p[5] << 8);
//if (epSize > 64) epSize = 64; // Workaround
if (epType == 2 && (epAddr & 0xF0) == 0x00) { // Bulk OUT
txpipe = new_Pipe(dev, 2, epAddr, 0, epSize);
} else if (epType == 2 && (epAddr & 0xF0) == 0x80) { // Bulk IN
rxpipe = new_Pipe(dev, 2, epAddr & 0x0F, 1, epSize);
}
}
p += descriptorLength;
}
if (rxpipe && txpipe) {
rxpipe->callback_function = rx_callback;
txpipe->callback_function = tx_callback;
txhead = 0;
txtail = 0;
//rxhead = 0;
//rxtail = 0;
memset(txbuffer, 0, sizeof(txbuffer));
first_update = true;
txready = true;
updatetimer.start(500000);
queue_Data_Transfer(rxpipe, rxpacket, 64, this);
rxlen = 0;
do_polling = false;
return true;
}
return false;
}
void TonexOne::disconnect()
{
updatetimer.stop();
//txtimer.stop();
}
void TonexOne::rx_callback(const Transfer_t *transfer)
{
if (!transfer->driver) return;
((TonexOne *)(transfer->driver))->rx_data(transfer);
}
void TonexOne::tx_callback(const Transfer_t *transfer)
{
println("TonexOne Send");
if (!transfer->driver) return;
((TonexOne *)(transfer->driver))->tx_data(transfer);
}
void TonexOne::rx_data(const Transfer_t *transfer)
{
println("TonexOne Receive");
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
print_hexbytes(transfer->buffer, len);
if (len < 1 || len > 64) {
queue_Data_Transfer(rxpipe, rxpacket, 64, this);
rxlen = 0;
} else {
rxlen = len; // signal arrival of data to Task()
// TODO: should someday use EventResponder to call from yield()
}
}
void TonexOne::tx_data(const Transfer_t *transfer)
{
uint8_t *p = (uint8_t *)transfer->buffer;
//print("tx_data, len=", *(p-1));
//print(", tail=", (p-1) - txbuffer);
//println(", tail=", txtail);
uint32_t tail = txtail;
uint8_t size = *(p - 1);
tail += size + 1;
if (tail >= sizeof(txbuffer)) tail -= sizeof(txbuffer);
txtail = tail;
//println("new tail=", tail);
txready = true;
transmit();
//txtimer.start(8000);
// adjust tail...
// start timer if more data to send
}
size_t TonexOne::write(const void *data, const size_t size)
{
print("write ", size);
print(" bytes: ");
print_hexbytes(data, size);
uint32_t head = txhead;
if (++head >= sizeof(txbuffer)) head = 0;
uint32_t remain = sizeof(txbuffer) - head;
if (remain < size + 1) {
// not enough space at end of buffer
txbuffer[head] = 0xFF;
head = 0;
}
uint32_t avail;
do {
uint32_t tail = txtail;
if (head > tail) {
avail = sizeof(txbuffer) - head + tail;
} else {
avail = tail - head;
}
} while (avail < size + 1); // wait for space in buffer
txbuffer[head] = size;
memcpy(txbuffer + head + 1, data, size);
txhead = head + size;
print("head=", txhead);
println(", tail=", txtail);
print_hexbytes(txbuffer, 60);
NVIC_DISABLE_IRQ(IRQ_USBHS);
transmit();
NVIC_ENABLE_IRQ(IRQ_USBHS);
return size;
}
void TonexOne::transmit()
{
if (!txready) return;
uint32_t head = txhead;
uint32_t tail = txtail;
if (head == tail) {
println("no data to transmit");
return; // no data to transit
}
if (++tail >= sizeof(txbuffer)) tail = 0;
uint32_t size = txbuffer[tail];
//print("tail=", tail);
//println(", tx size=", size);
if (size == 0xFF) {
txtail = 0;
tail = 0;
size = txbuffer[0];
println("tx size=", size);
}
//txtail = tail + size;
queue_Data_Transfer(txpipe, txbuffer + tail + 1, size, this);
//txtimer.start(8000);
txready = false;
}
void TonexOne::Send_hello() {
uint8_t hello[17] = { 0x7e, 0xb9, 0x03, 0x00, 0x82, 0x04, 0x00, 0x80, 0x0b, 0x01, 0xb9, 0x02, 0x02, 0x0b, 0x17, 0x8c, 0x7e};
write(hello, 17);
}
void TonexOne::Request_state() {
uint8_t request_state[19] = {0x7e, 0xb9, 0x03, 0x00, 0x82, 0x06, 0x00, 0x80, 0x0b, 0x03, 0xb9, 0x02, 0x81, 0x06, 0x03, 0x0b, 0x44, 0x66, 0x7e };
write(request_state, 19);
}
void TonexOne::Request_data_part1(uint8_t patch) {
uint8_t request[14] = {0xb9, 0x03, 0x82, 0x06, 0x00, 0x80, 0x0b, 0x03, 0xb9, 0x04, 0x0b, 0x01, patch, 0x00};
uint8_t out_msg[20];
uint8_t out_len = addFraming(request, 14, out_msg);
write(out_msg, out_len);
}
void TonexOne::Request_data_part2(uint8_t patch) {
uint8_t request[14] = {0xb9, 0x03, 0x82, 0x06, 0x00, 0x80, 0x0b, 0x03, 0xb9, 0x04, 0x0b, 0x01, patch, 0x01};
uint8_t out_msg[20];
uint8_t out_len = addFraming(request, 14, out_msg);
write(out_msg, out_len);
}
void TonexOne::SetPatch(uint8_t number_slot1, uint8_t number_slot2, uint8_t number_slot3, uint8_t active_slot)
{
if (number_slot1 > 19) return;
if (number_slot2 > 19) return;
if (number_slot3 > 19) return;
if (active_slot > 2) return;
uint8_t in_msg[166] = { 0xB9, 0x03, 0x81, 0x06, 0x03, 0x82, 0x9B, 0x00, 0x80, 0x0B, 0x03, 0xB9, 0x01, 0xB9, 0x0D,
0x88, 0x00, 0x00, 0x00, 0x3F, 0x88, 0x00, 0x00, 0x20, 0x41, 0x01, 0x00, 0x01, 0xBA, 0x14, 0xB9,
0x03, 0x2F, 0x00, 0x80, 0xFF, 0xB9, 0x03, 0x00, 0x80, 0xFF, 0x00, 0xB9, 0x03, 0x00, 0x80, 0xFF,
0x00, 0xB9, 0x03, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0xB9, 0x03, 0x0F, 0x80, 0xFF, 0x2F, 0xB9, 0x03,
0x80, 0xFF, 0x00, 0x00, 0xB9, 0x03, 0x00, 0x00, 0x80, 0xFF, 0xB9, 0x03, 0x80, 0xBF, 0x80, 0xBF,
0x80, 0xBF, 0xB9, 0x03, 0x80, 0x9F, 0x80, 0xFF, 0x00, 0xB9, 0x03, 0x00, 0x80, 0xFF, 0x80, 0xFF,
0xB9, 0x03, 0x11, 0x11, 0x00, 0xB9, 0x03, 0x80, 0xFF, 0x00, 0x00, 0xB9, 0x03, 0x00, 0x11, 0x00,
0xB9, 0x03, 0x0A, 0x00, 0x0A, 0xB9, 0x03, 0x00, 0x22, 0x06, 0xB9, 0x03, 0x11, 0x00, 0x00, 0xB9,
0x03, 0x00, 0x00, 0x11, 0xB9, 0x03, 0x0B, 0x0B, 0x0B, 0xB9, 0x03, 0x11, 0x22, 0x00, 0xB9, 0x03,
0x00, 0x19, 0x19, 0xBC, 0x06, 0, 0x00, 0, 0x00, 0, 0x00, 0x00, 0, 0x81, 0xB8, 0x01,
0x01, 0x00, 0x88, 0x00, 0x00, 0xF0, 0x42};
in_msg[148] = number_slot1;
in_msg[150] = number_slot2;
in_msg[152] = number_slot3;
in_msg[155] = active_slot;
uint8_t out_msg[180];
uint8_t out_len = addFraming(in_msg, 166, out_msg);
write(out_msg, out_len);
}
uint16_t TonexOne::addByteWithStuffing(uint8_t* output, uint8_t byte)
{
uint16_t length = 0;
if (byte == 0x7E || byte == 0x7D)
{
output[length] = 0x7D;
length++;
output[length] = byte ^ 0x20;
length++;
}
else
{
output[length] = byte;
length++;
}
return length;
}
uint16_t TonexOne::calculateCRC(uint8_t* data, uint16_t length) // CRC-16-CCITT (Kermit)
{
uint16_t crc = 0xFFFF;
for (uint16_t loop = 0; loop < length; loop++)
{
crc ^= data[loop];
for (uint8_t i = 0; i < 8; ++i)
{
if (crc & 1)
{
crc = (crc >> 1) ^ 0x8408; // 0x8408 is the reversed polynomial x^16 + x^12 + x^5 + 1
}
else
{
crc = crc >> 1;
}
}
}
return ~crc;
}
uint16_t TonexOne::addFraming(uint8_t* input, uint16_t inlength, uint8_t* output)
{
uint16_t outlength = 0;
// Start flag
output[outlength] = 0x7E;
outlength++;
// add input bytes
for (uint16_t byte = 0; byte < inlength; byte++)
{
outlength += addByteWithStuffing(&output[outlength], input[byte]);
}
// add CRC
uint16_t crc = calculateCRC(input, inlength);
outlength += addByteWithStuffing(&output[outlength], crc & 0xFF);
outlength += addByteWithStuffing(&output[outlength], (crc >> 8) & 0xFF);
// End flag
output[outlength] = 0x7E;
outlength++;
return outlength;
}
Last edited: