HP Forums

Full Version: Interfacing the Prime with a cheap MAX3421E host controller (DIY Wireless dongle?)
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
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]

#include <Usb.h>

#define EP_MAXPKTSIZE           64 // max size for data via USB
#define EP_INTERRUPT            0x03

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

#define PRIME_VID 1008
#define PRIME_PID 1089

class PrimeUsb:USBDeviceConfig
  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;

  USB *pUsb;
  uint8_t bAddress;

  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() {
   pFuncOnInit(); // Call the user function*/

/* Performs a cleanup after failed Init() attempt */
uint8_t PrimeUsb::Release() {
  PrimeConnected = false;
  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() {
    return 0;

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

    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();
  Notify(PSTR("\r\nPrime Init"), 0x80);

  // check if address has already been assigned to an instance
  if(bAddress) {
    Notify(PSTR("\r\nAddress in use"), 0x80);

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

  if(!p) {
    Notify(PSTR("\r\nAddress not found"), 0x80);

  if(!p->epinfo) {
    Notify(PSTR("\r\nepinfo is null"), 0x80);

  // 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;

    goto FailGetDevDescr;

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

    goto FailUnknownDevice;

  // We are connected
  PrimeConnected = true;

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


  // 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;
    bAddress = 0;
    Notify(PSTR("\r\nsetAddr: "), 0x80);
    D_PrintHex<uint8_t > (rcode, 0x80);

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

  p->lowspeed = false;

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

  p->lowspeed = lowspeed;

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

  /* Initialize data structures for endpoints of device */
  epInfo[ PRIME_OUTPUT_PIPE ].epAddr = 0x2; // output endpoint
  epInfo[ PRIME_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
  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 ].bmSndToggle = 0;
  epInfo[ PRIME_INPUT_PIPE ].bmRcvToggle = 0;

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

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

  rcode = pUsb->setConf(bAddress, epInfo[ PRIME_CONTROL_PIPE ].epAddr, 1);
    goto FailSetConfDescr;

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

  /* Diagnostic messages */
  goto Fail;

  goto Fail;

  goto Fail;

  NotifyFailUnknownDevice(VID, PID);

  Notify(PSTR("\r\nInit Failed, error code: "), 0x80);
  return rcode;

USB Usb;
PrimeUsb myPrime(&Usb);

void setup()
  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()

Looks good, go on Wink

FWIW, your test packet contains trailing garbage.
Sure, the garbage is directly from a received package *-* (copy&'paste as array' in Hex Workshop Tongue)
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.
(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
Reference URL's