Not receiving data from external USB device in USBhost_t36

sixeight

Well-known member
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:
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:
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:
...
I wrote something similar but for the Source Audio C4 Synth pedal here: https://github.com/MichaelMCE/TeensyC4Synth. With the USB stuff in c4_drv.cpp.
Why is there a queue_Data_Transfer() read during claim?
Generally how it goes it thus: Write a cmd then wait for the response.
 
I wrote something similar but for the Source Audio C4 Synth pedal here: https://github.com/MichaelMCE/TeensyC4Synth. With the USB stuff in c4_drv.cpp.
Why is there a queue_Data_Transfer() read during claim?
Generally how it goes it thus: Write a cmd then wait for the response.
Thanks for the response. I removed the queue_Data_transfer. It is not neccesary. Copied it from other code. But it makes no difference.
I notice two things: the transmit queue is hanging after it is full. So data is not removed. And the receive queue is not working. I am still trying to find my way around the USBhost_t36 library. Is this a problem that is related?
 
Back
Top