Post Reply 
Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
02-11-2014, 07:15 PM (This post was last modified: 02-12-2014 09:19 PM by eried.)
Post: #1
Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
The firmware still needs a lot of work, this is only a test. Also I still don't decide if to use a very cheap 433/333 mhz RF module or just a plain 2.4ghz one.

[Image: 0fec0a86-9350-11e3-9fbe-6ff3a0bc06f3.jpg]

Code:
#include <Usb.h>

#define EP_MAXPKTSIZE           64 // max size for data via USB
#define EP_INTERRUPT            0x03
#define HID_REQUEST_GET_REPORT  0x01
#define HID_REQUEST_SET_REPORT  0x09
#define PRIME_MAX_ENDPOINTS     2

#define PRIME_CONTROL_PIPE        0
#define PRIME_OUTPUT_PIPE         1
#define PRIME_INPUT_PIPE          2
#define PRIME_REPORT_BUFFER_SIZE  64

#define PRIME_VID 1008
#define PRIME_PID 1089
#define DEBUG_USB_HOST

class PrimeUsb:USBDeviceConfig
{
public:
  PrimeUsb (USB*);
  virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);

  virtual uint8_t Release();
  virtual uint8_t Poll();
  virtual uint8_t GetAddress() { return bAddress; };
  void attachOnInit(void (*funcOnInit)(void)) { pFuncOnInit = funcOnInit; };
  bool PrimeConnected;

protected:
  USB *pUsb;
  uint8_t bAddress;
  EpInfo epInfo[PRIME_MAX_ENDPOINTS];

private:
  void onInit();
  void (*pFuncOnInit)(void); // Pointer to function called in onInit()

  bool bPollEnable;
  uint32_t timer;

  uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
  uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data

  void readReport(); // read incoming data
  void printReport(); // print incoming date - Uncomment for debugging
};

PrimeUsb::PrimeUsb (USB *p):pUsb(p)
{
  for(uint8_t i = 0; i < PRIME_MAX_ENDPOINTS; i++) {
    epInfo[i].epAddr = 0;
    epInfo[i].maxPktSize = (i) ? 0 : 8;
    epInfo[i].epAttribs = 0;
    epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
  }

  if(pUsb) // register in USB subsystem
    pUsb->RegisterDeviceClass(this); //set devConfig[] entry
}

void PrimeUsb::onInit() {
  /*if(pFuncOnInit)
   pFuncOnInit(); // Call the user function*/
}


/* Performs a cleanup after failed Init() attempt */
uint8_t PrimeUsb::Release() {
  PrimeConnected = false;
  pUsb->GetAddressPool().FreeAddress(bAddress);
  bAddress = 0;
  bPollEnable = false;
  return 0;
}

uint8_t test[EP_MAXPKTSIZE] =
{
  0x00, 0xF2, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x48, 0x00, 0x45, 0x00, 0x4C, 0x00, 0x4C, 0x00, 0x4F,
  0x00, 0x00, 0x00, 0x31, 0x78, 0x5E, 0x27, 0x31, 0x06, 0x10, 0x00, 0x00, 0xF0, 0x95, 0x37, 0x30,
  0x14, 0x91, 0x6D, 0x31, 0x94, 0x90, 0x6D, 0x31, 0x94, 0x91, 0x6D, 0x31, 0xE4, 0x91, 0x6D, 0x31,
  0x00, 0x4E, 0x27, 0x31, 0x74, 0x5E, 0x27, 0x31, 0x78, 0x5E, 0x27, 0x31, 0x00, 0x00, 0x00, 0x00,
};

uint8_t PrimeUsb::Poll() {
  if(!bPollEnable)
    return 0;

  if(PrimeConnected)
  {
    uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
    pUsb->inTransfer(bAddress, epInfo[ PRIME_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
    if(millis() - timer > 100)
    { // Loop 100ms before processing data
      readReport();
      printReport();
    }

    if(millis() - timer > 4000) { // Send at least every 4th second
      Notify(PSTR("\r\nPreparing..."), 0x80);

      pUsb->outTransfer(bAddress, epInfo[ PRIME_OUTPUT_PIPE ].epAddr, EP_MAXPKTSIZE, test);
      timer = millis();
      Notify(PSTR("\r\nSending report..."), 0x80);
    }
  }
}


void PrimeUsb::readReport() {
  //ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16));
  Notify(PSTR("\r\nReading report..."), 0x80);
}

void PrimeUsb::printReport() { 
  for(uint8_t i = 0; i < PRIME_REPORT_BUFFER_SIZE; i++) {
    D_PrintHex<uint8_t > (readBuf[i], 0x80);
    Notify(PSTR(" "), 0x80);
  }
  Notify(PSTR("\r\n"), 0x80);
}

uint8_t PrimeUsb::Init(uint8_t parent, uint8_t port, bool lowspeed) {
  uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
  uint8_t rcode;
  UsbDevice *p = NULL;
  EpInfo *oldep_ptr = NULL;
  uint16_t PID;
  uint16_t VID;

  // get memory address of USB device address pool
  AddressPool &addrPool = pUsb->GetAddressPool();
#ifdef DEBUG_USB_HOST
  Notify(PSTR("\r\nPrime Init"), 0x80);
#endif

  // check if address has already been assigned to an instance
  if(bAddress) {
#ifdef DEBUG_USB_HOST
    Notify(PSTR("\r\nAddress in use"), 0x80);
#endif
    return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
  }

  // Get pointer to pseudo device with address 0 assigned
  p = addrPool.GetUsbDevicePtr(0);

  if(!p) {
#ifdef DEBUG_USB_HOST
    Notify(PSTR("\r\nAddress not found"), 0x80);
#endif
    return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
  }

  if(!p->epinfo) {
#ifdef DEBUG_USB_HOST
    Notify(PSTR("\r\nepinfo is null"), 0x80);
#endif
    return USB_ERROR_EPINFO_IS_NULL;
  }

  // Save old pointer to EP_RECORD of address 0
  oldep_ptr = p->epinfo;

  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
  p->epinfo = epInfo;
  p->lowspeed = lowspeed;

  // Get device descriptor
  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
  // Restore p->epinfo
  p->epinfo = oldep_ptr;

  if(rcode)
    goto FailGetDevDescr;

  VID = udd->idVendor;
  PID = udd->idProduct;

  if(VID != PRIME_VID || PID != PRIME_PID)
    goto FailUnknownDevice;

  // We are connected
  PrimeConnected = true;

  // Allocate new address according to device class
  bAddress = addrPool.AllocAddress(parent, false, port);

  if(!bAddress)
    return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;

  // Extract Max Packet Size from device descriptor
  epInfo[0].maxPktSize = udd->bMaxPacketSize0;

  // Assign new address to the device
  rcode = pUsb->setAddr(0, 0, bAddress);

  if(rcode) {
    p->lowspeed = false;
    addrPool.FreeAddress(bAddress);
    bAddress = 0;
#ifdef DEBUG_USB_HOST
    Notify(PSTR("\r\nsetAddr: "), 0x80);
    D_PrintHex<uint8_t > (rcode, 0x80);
#endif

    return rcode;
  }
#ifdef EXTRADEBUG
  Notify(PSTR("\r\nAddr: "), 0x80);
  D_PrintHex<uint8_t > (bAddress, 0x80);
#endif
  //delay(300); // Spec says you should wait at least 200ms

  p->lowspeed = false;

  //get pointer to assigned address record
  p = addrPool.GetUsbDevicePtr(bAddress);
  if(!p)
    return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;

  p->lowspeed = lowspeed;

  // Assign epInfo to epinfo pointer - only EP0 is known
  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
  if(rcode)
    goto FailSetDevTblEntry;

  /* Initialize data structures for endpoints of device */
  epInfo[ PRIME_OUTPUT_PIPE ].epAddr = 0x2; // output endpoint
  epInfo[ PRIME_OUTPUT_PIPE ].epAttribs = EP_INTERRUPT;
  epInfo[ PRIME_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
  epInfo[ PRIME_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
  epInfo[ PRIME_OUTPUT_PIPE ].bmSndToggle = 0;
  epInfo[ PRIME_OUTPUT_PIPE ].bmRcvToggle = 0;
  epInfo[ PRIME_INPUT_PIPE ].epAddr = 0x01; // report endpoint
  epInfo[ PRIME_INPUT_PIPE ].epAttribs = EP_INTERRUPT;
  epInfo[ PRIME_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
  epInfo[ PRIME_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
  epInfo[ PRIME_INPUT_PIPE ].bmSndToggle = 0;
  epInfo[ PRIME_INPUT_PIPE ].bmRcvToggle = 0;

  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
  if(rcode)
    goto FailSetDevTblEntry;

  delay(200); //Give time for address change

  rcode = pUsb->setConf(bAddress, epInfo[ PRIME_CONTROL_PIPE ].epAddr, 1);
  if(rcode)
    goto FailSetConfDescr;
  Serial.println("here");
  onInit();

  bPollEnable = true;
  Notify(PSTR("\r\n"), 0x80);
  timer = millis();
  return 0; // Successful configuration

  /* Diagnostic messages */
FailGetDevDescr:
#ifdef DEBUG_USB_HOST
  NotifyFailGetDevDescr();
  goto Fail;
#endif

FailSetDevTblEntry:
#ifdef DEBUG_USB_HOST
  NotifyFailSetDevTblEntry();
  goto Fail;
#endif

FailSetConfDescr:
#ifdef DEBUG_USB_HOST
  NotifyFailSetConfDescr();
#endif
  goto Fail;

FailUnknownDevice:
#ifdef DEBUG_USB_HOST
  NotifyFailUnknownDevice(VID, PID);
#endif
  rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;

Fail:
#ifdef DEBUG_USB_HOST
  Notify(PSTR("\r\nInit Failed, error code: "), 0x80);
  NotifyFail(rcode);
#endif
  Release();
  return rcode;
}


USB Usb;
PrimeUsb myPrime(&Usb);

void setup()
{
  Serial.begin(115200);
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); //halt
  }
  Serial.print(F("\r\nPrime USB Library Started"));
}

void loop()
{
  Usb.Task();
}




My website: erwin.ried.cl
Visit this user's website Find all posts by this user
Quote this message in a reply
02-11-2014, 07:36 PM
Post: #2
Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
Looks good, go on Wink

FWIW, your test packet contains trailing garbage.
Find all posts by this user
Quote this message in a reply
02-11-2014, 07:44 PM
Post: #3
RE: Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
Sure, the garbage is directly from a received package *-* (copy&'paste as array' in Hex Workshop Tongue)

My website: erwin.ried.cl
Visit this user's website Find all posts by this user
Quote this message in a reply
02-11-2014, 09:05 PM
Post: #4
RE: Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
Excellent project. And very convenient to have one less cable on the table Smile

I will go back frequently to see the progress.

A hug from Argentina.
Find all posts by this user
Quote this message in a reply
02-12-2014, 09:18 PM
Post: #5
RE: Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
(02-11-2014 09:05 PM)ArielPalazzesi Wrote:  Excellent project. And very convenient to have one less cable on the table Smile

There is a long way to reach that convenience Big Grin (design a custom pcb/3d printable case, etc) this is more like a small challenge than the search for avoiding the extra usb cable.





Also maybe HP marketing team one day see some business in a DIY (datastreamer+wireless dongle) pack? Angel

My website: erwin.ried.cl
Visit this user's website Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 1 Guest(s)