[posted] MIDI USB Host to MIDI Din converter

XFer

Well-known member
I wanted to play my MIDI hardware synths with a cheap small portable MIDI USB keyboard.

There are a few MIDI USB Host converters on the market, but they tend to be pricey and not so flexible; for example, the MIDItech USB Host converter I purchased (at $80+ ) does not filter "Keep Alive" MIDI messages, which cause issues with many older MIDI synth modules. Moreover, when you connect some MIDI USB keyboards, they send spurious junk over MIDI for the first second or so, again causing issues to some hardware modules.

So this is it: a converter powered by the phenomenal Teensy 3.6. I did not need processing power and wanted to save battery, so I downlocked it to 24 MHz, more than enough.

Connected to a smartphone USB powerbank, it runs for many hours while powering the USB keyboard.

Thanks to my dear friend Alessandro for the soldering work and the 3D printed case.

full.jpg

side_microusb.jpg

side_mididin.jpg

front_usbhost.jpg

open.jpg

in_action.jpg

For the hardware part, I just followed the instructions on PJRC website:
https://www.pjrc.com/teensy/td_libs_MIDI.html

Added a couple LEDs to signal errors and MIDI connection

Software is not really complete, but works OK for my limited needs:

Code:
// v0.7.1
// first version 20180121, last modified 20200209-1
// last comments 20200425-1

// TODO: 
// Check if latency improves with higher CPU clocks (now 24 MHz to save power)
// Measure how much current is drawn by 5V microUSB when various keyboards (Akai LPK25, Miditech37, ProDipe49, Korg MiniKey etc.) are connected:
//  CPU @ 24 MHz
//  Miditech37: total current (converted + keyboard) = 65mA (340 mW)
//  Jammin Pro PK25: total current = 65mA (340 mW)
// Handle "SysEx chunks" (not we only have a debug placeholder)
// Add bridge from microUSB_input 5V and GND pins to USB Host out, to get more power (but check Teensy 3.6 schematics first! Where does USB Host get power from? Do we risk frying anything?)
// Add PANIC button (all notes off): send Control Change message 123 (not SysEx!) on all channels

// Don't need to set "MIDI" under USB Type; "Serial" works OK and allows for VisualMicro SerialMonitor to work (MIDI + Serial works as well, but MIDI is not needed since we only use USB Host MIDI, not microUSB MIDI)
// Reference: https://www.pjrc.com/teensy/td_midi.html
// Wiring: v. https://www.pjrc.com/teensy/td_libs_MIDI.html
// Simplified version (MIDI out only):
// MIDI female pin 4 to +3.3V via 47 Ohm (replace with 33 Ohm should problems arise)
// MIDI female pin 5 to Teensy pin 10 (Serial2 TX) via 47 Ohm  (replace with 33 Ohm should problems arise)
// MIDI female pin 2 to GND
// v. https://forum.pjrc.com/threads/49282-Teensy-3-6-as-USBMIDI-to-SerialMIDI-adapter
// NOTE: USB Host cable must be plugged with +5V at top position. No problem if the fifth pin is not connected (redundant GND)
// PCB: Blue pin	= Serial2TX via 47 Ohm (to MIDI pin 5)
//		Yellow pin	= +3.3V via 47 Ohm (to MIDI pin 4)
//		White pin	= pin 6 via 4.7 KOhm (to white "running" LED). White LED draws 160uA at 3.4V = 545mW ; stays lit all the time a keyboard is connected
//		Orange pin	= pin 5 via 1 KOhm (to yellow "no input device" LED). Orange LED draws 1435 uA at 3.4V = 4.88mW; stays lit when a keyboard is not connected (so typically just a few seconds)
//		Red pins	= +3.3V (unused)
//		Black pins  = GND (to MIDI pin 2 and LED negative pins)

#include <USBHost_t36.h>	// Access to USB Host MIDI
#include <MIDI.h>			// access to serial (5 pin DIN) MIDI

#define MIDIADAPTER_VERSION "0.7.1"
#define HW_SERIAL_PORT Serial2
#define SERIALDEBUG_TIMEOUT_MS 5000	// After that, it starts even if no serial debug port active
#define SETUP_DELAY_MS 1500
#define KEYBOARD_CHECK_INTERVAL_MS 200
#define DISCARD_INITIAL_JUNK_INTERVAL_MS 1500
#define SERIAL_DEBUG_SPEED 115200
#define ERROR_LED_PIN LED_BUILTIN
#define NO_INPUT_DEVICE_LED_PIN 5
#define RUNNING_LED_PIN 6

#define BLOCK_ACTIVE_SENSING	// Filter out annoying useless "I am alive" messages which mess up playing with many MIDI synths
#define ENABLE_RUNNING_LED		// Disable to save 0.5 milliwatts
//#define SERIALDEBUG


MIDI_CREATE_INSTANCE(HardwareSerial, HW_SERIAL_PORT, MIDI);

/////////////
// GLOBALS
/////////////

USBHost gUSBHostPort;		// g prefix for "global"
USBHub gUSBHostHub(gUSBHostPort);
MIDIDevice gInputMIDIUSBDevice(gUSBHostPort);
uint32_t gMIDIjunkStartTime;
uint32_t gSerialStartTime;

//////////
// MISC
//////////

static void printBytes(const byte *data, unsigned int size) 
{
	while (size > 0) 
	{
		byte b = *data++;
		if (b < 16) Serial.print('0');
		Serial.print(b, HEX);
		if (size > 1) Serial.print(' ');
		size = size - 1;
	}
}


//////////
// LEDS
//////////

static void initializeLEDs()
{
	pinMode(ERROR_LED_PIN, OUTPUT);
	pinMode(NO_INPUT_DEVICE_LED_PIN, OUTPUT);
#ifdef ENABLE_RUNNING_LED
	pinMode(RUNNING_LED_PIN, OUTPUT);
#endif
}

// Error LEDs turn on when a MIDI event is not handled (an appropriate event handler has yet to be written)
static void errorLED_On()
{
	digitalWriteFast(ERROR_LED_PIN, HIGH);
}

static void errorLED_Off()
{
	digitalWriteFast(ERROR_LED_PIN, LOW);
}

static void noDeviceLED_On()
{
	digitalWriteFast(NO_INPUT_DEVICE_LED_PIN, HIGH);
}

static void noDeviceLED_Off()
{
	digitalWriteFast(NO_INPUT_DEVICE_LED_PIN, LOW);
}

static void runningLED_On()
{
	digitalWriteFast(RUNNING_LED_PIN, HIGH);
}

static void runningLED_Off()
{
	digitalWriteFast(RUNNING_LED_PIN, LOW);
}


/////////////////////////////////////////////
// WAIT FOR INPUT DEVICE (on USB Host port)
/////////////////////////////////////////////

static void waitForUSBMIDIdevice()
{
#ifdef ENABLE_RUNNING_LED
	runningLED_Off();
#endif
	noDeviceLED_On();	// To save battery power, external yellow LED starts ON and turns off when a USB MIDI keyboard is detected

	while (!gInputMIDIUSBDevice)
	{
		//Snooze.sleep(gSnoozeConfig);	// Idea was saving battery while waiting for USB device, but won't work (disconnects USB port)
		delay(KEYBOARD_CHECK_INTERVAL_MS);
	}

	noDeviceLED_Off();	// Input connected: turn off LED
#ifdef ENABLE_RUNNING_LED
	runningLED_On();
#endif
	startJunkTimer();
}


///////////////////////////
// MIDI MESSAGE HANDLERS
///////////////////////////

static void myNoteOn(byte channel, byte note, byte velocity) 
{
	// When a USB device with multiple virtual cables is used,
	// gInputMIDIUSBDevice.getCable() can be used to read which of the virtual
	// MIDI cables received this message.
	errorLED_Off();	// This event handler is implemented: no need to signal an error
#ifdef SERIALDEBUG
		Serial.print("Note On, ch=");
		Serial.print(channel, DEC);
		Serial.print(", note=");
		Serial.print(note, DEC);
		Serial.print(", velocity=");
		Serial.println(velocity, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendNoteOn(note, velocity, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myNoteOff(byte channel, byte note, byte velocity) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Note Off, ch=");
	Serial.print(channel, DEC);
	Serial.print(", note=");
	Serial.print(note, DEC);
	Serial.print(", velocity=");
	Serial.println(velocity, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendNoteOff(note, velocity, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myAfterTouchPoly(byte channel, byte note, byte velocity) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("AfterTouch Change, ch=");
	Serial.print(channel, DEC);
	Serial.print(", note=");
	Serial.print(note, DEC);
	Serial.print(", velocity=");
	Serial.println(velocity, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendAfterTouch(note, velocity, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myControlChange(byte channel, byte control, byte value) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Control Change, ch=");
	Serial.print(channel, DEC);
	Serial.print(", control=");
	Serial.print(control, DEC);
	Serial.print(", value=");
	Serial.println(value, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendControlChange(control, value, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myProgramChange(byte channel, byte program) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Program Change, ch=");
	Serial.print(channel, DEC);
	Serial.print(", program=");
	Serial.println(program, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendProgramChange(program, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myAfterTouchChannel(byte channel, byte pressure) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("After Touch, ch=");
	Serial.print(channel, DEC);
	Serial.print(", pressure=");
	Serial.println(pressure, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendAfterTouch(pressure, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myPitchChange(byte channel, int pitch) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Pitch Change, ch=");
	Serial.print(channel, DEC);
	Serial.print(", pitch=");
	Serial.println(pitch, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendPitchBend(pitch, channel);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}


// This 3-input System Exclusive function is more complex, but allows you to
// process very large messages which do not fully fit within the gInputMIDIUSBDevice's
// internal buffer.  Large messages are given to you in chunks, with the
// 3rd parameter to tell you which is the last chunk.  This function is
// a Teensy extension, not available in the Arduino MIDI library.
//
static void mySystemExclusiveChunk(const byte *data, uint16_t length, bool last) 
{
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		errorLED_On();	// TODO This event handler is not implemented yet!
	}
#ifdef SERIALDEBUG
	Serial.print("SysEx Chunk (not implemented!): ");
	printBytes(data, length);
	if (last) 
	{
		Serial.println(" (end)");
	}
	else 
	{
		Serial.println(" (to be continued)");
	}
#endif
	// TODO how to proceed? Maybe buffering to a global array and increment length (global) 
	// until we are called with last = true, then we send the global buffer via MIDI.sendSysEx()?
}

// This simpler 2-input System Exclusive function can only receive messages
// up to the size of the internal buffer.  Larger messages are truncated, with
// no way to receive the data which did not fit in the buffer.  If both types
// of SysEx functions are set, the 3-input version will be called by gInputMIDIUSBDevice.
//
static void mySystemExclusive(byte *data, unsigned int length) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("SysEx Message: ");
	printBytes(data, length);
	Serial.println();
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendSysEx(length, data);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myTimeCodeQuarterFrame(byte data) 
{
	errorLED_Off();	// This event handler is implemented
	static char SMPTE[8] = { '0','0','0','0','0','0','0','0' };
	static byte fps = 0;
	byte index = data >> 4;
	byte number = data & 15;
	if (index == 7) {
		fps = (number >> 1) & 3;
		number = number & 1;
	}
	if (index < 8 || number < 10)
	{
		SMPTE[index] = number + '0';
#ifdef SERIALDEBUG
		Serial.print("TimeCode: ");  // perhaps only print when index == 7
		Serial.print(SMPTE[7]);
		Serial.print(SMPTE[6]);
		Serial.print(':');
		Serial.print(SMPTE[5]);
		Serial.print(SMPTE[4]);
		Serial.print(':');
		Serial.print(SMPTE[3]);
		Serial.print(SMPTE[2]);
		Serial.print('.');
		Serial.print(SMPTE[1]);  // perhaps add 2 to compensate for MIDI latency?
		Serial.print(SMPTE[0]);
		switch (fps) 
		{
		case 0: Serial.println(" 24 fps"); break;
		case 1: Serial.println(" 25 fps"); break;
		case 2: Serial.println(" 29.97 fps"); break;
		case 3: Serial.println(" 30 fps"); break;
		default: Serial.println(" Unsupported FPS!"); break;	// TODO should turn on ErrorLED
		}
	}
	else 
	{
		Serial.print("TimeCode: invalid data = ");	// TODO should turn on ErrorLED
		Serial.println(data, HEX);
	}
#else
}
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendTimeCodeQuarterFrame(data);	// TODO not sure about this!
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void mySongPosition(uint16_t beats)
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Song Position, beat=");
	Serial.println(beats);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendSongPosition(beats);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void mySongSelect(byte songNumber)
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Song Select, song=");
	Serial.println(songNumber, DEC);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendSongSelect(songNumber);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myTuneRequest()
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Tune Request");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendTuneRequest();
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myClock() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Clock");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::Clock);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myStart() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Start");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::Start);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myContinue() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Continue");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::Continue);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myStop() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Stop");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::Stop);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myActiveSensing() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("Active Sensing");
#endif
#ifndef BLOCK_ACTIVE_SENSING
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::ActiveSensing);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
#else
#ifdef SERIALDEBUG
	Serial.println("Blocked by filter");
#endif
#endif
}

static void mySystemReset() 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.println("System Reset");
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime(midi::SystemReset);	// TODO ...not sure...?
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void myRealTimeSystem(uint8_t realtimebyte) 
{
	errorLED_Off();	// This event handler is implemented
#ifdef SERIALDEBUG
	Serial.print("Real Time Message, code=");
	Serial.println(realtimebyte, HEX);
#endif
	if (elapsedJunkTimer())	// Shortly after connected/reconnected, most MIDI input devices send junk commands: ignore them
	{
		MIDI.sendRealTime((midi::MidiType) realtimebyte);
	}
	else
	{
#ifdef SERIALDEBUG
		Serial.println("Blocked by junk timer");
#endif
	}
}

static void startJunkTimer()
{
	gMIDIjunkStartTime = millis();
}


static bool elapsedJunkTimer()
{
	if (millis() - gMIDIjunkStartTime > DISCARD_INITIAL_JUNK_INTERVAL_MS)
		return true;
	return false;
}

///////////////
//
//   SETUP
//
///////////////

void setup() 
{   
	initializeLEDs();

  // configure wakeup timer
	//gWakeupTimer.setTimer(KEYBOARD_CHECK_INTERVAL_MS);

#ifdef SERIALDEBUG
	Serial.begin(SERIAL_DEBUG_SPEED);
#endif

// Wait 1.5 seconds before turning on USB Host.  If connected USB devices
// use too much power, Teensy at least completes USB enumeration, which
// makes isolating the power issue easier.
	delay(SETUP_DELAY_MS);

#ifdef SERIALDEBUG
	gSerialStartTime = millis();
	while (!Serial && (millis() - gSerialStartTime < SERIALDEBUG_TIMEOUT_MS))
	{
		delay(10);
	}
	Serial.print("USB MIDI Adapter v.");
	Serial.println(MIDIADAPTER_VERSION);
	delay(100);
#endif

	MIDI.begin(); // Channel indication not needed in this case
  
	gUSBHostPort.begin();

#ifdef SERIALDEBUG
  Serial.println("Waiting for USB MIDI input device...");
#endif
  
	waitForUSBMIDIdevice();

	// At this point, a device has been connected

#ifdef SERIALDEBUG
  Serial.println("USB MIDI input device connected");
#endif

	gInputMIDIUSBDevice.setHandleNoteOn(myNoteOn);
	gInputMIDIUSBDevice.setHandleNoteOff(myNoteOff);
	gInputMIDIUSBDevice.setHandleAfterTouchPoly(myAfterTouchPoly);
	gInputMIDIUSBDevice.setHandleControlChange(myControlChange);
	gInputMIDIUSBDevice.setHandleProgramChange(myProgramChange);
	gInputMIDIUSBDevice.setHandleAfterTouchChannel(myAfterTouchChannel);
	gInputMIDIUSBDevice.setHandlePitchChange(myPitchChange);
	// Only one of these System Exclusive handlers will actually be
	// used.  See the comments below for the difference between them.
	gInputMIDIUSBDevice.setHandleSystemExclusive(mySystemExclusiveChunk);
	gInputMIDIUSBDevice.setHandleSystemExclusive(mySystemExclusive); 
	gInputMIDIUSBDevice.setHandleTimeCodeQuarterFrame(myTimeCodeQuarterFrame);
	gInputMIDIUSBDevice.setHandleSongPosition(mySongPosition);
	gInputMIDIUSBDevice.setHandleSongSelect(mySongSelect);
	gInputMIDIUSBDevice.setHandleTuneRequest(myTuneRequest);
	gInputMIDIUSBDevice.setHandleClock(myClock);
	gInputMIDIUSBDevice.setHandleStart(myStart);
	gInputMIDIUSBDevice.setHandleContinue(myContinue);
	gInputMIDIUSBDevice.setHandleStop(myStop);
	gInputMIDIUSBDevice.setHandleActiveSensing(myActiveSensing);
	gInputMIDIUSBDevice.setHandleSystemReset(mySystemReset);
	// This generic System Real Time handler is only used if the
	// more specific ones are not set.
	gInputMIDIUSBDevice.setHandleRealTimeSystem(myRealTimeSystem);
}


//////////////
//
//   LOOP
//
//////////////

void loop() 
{
  // The handler functions are called when gInputMIDIUSBDevice reads data.  They
  // will not be called automatically.  You must call gInputMIDIUSBDevice.read()
  // regularly from loop() for gInputMIDIUSBDevice to actually read incoming
  // data and run the handler functions as messages arrive.
  gUSBHostPort.Task();
  if (gInputMIDIUSBDevice)	// Input device could have been disconnected after a while (after setup()), so always check
  {
		gInputMIDIUSBDevice.read();
  }
  else
  {
		waitForUSBMIDIdevice();
  }	  
}

I'm infinitely grateful to PJRC and all of PJRC Forum members.
Thanks guys!
 
This is what it needs :)

Great job. However, I have a question. Is it possible to adapt the program to run under Teensy 2.0 or LC?

Sadly, compiling to the above causes an error:
Code:
In file included from ./Arduino/usb-midi-to-din-converter/usb-midi-to-din-converter.ino:31:0:
.arduino-1.8.19/hardware/teensy/avr/libraries/USBHost_t36/USBHost_t36.h:30:2: error: #error "USBHost_t36 only works with Teensy 3.6 or Teensy 4.x.  Please select it in Tools > Boards"
 #error "USBHost_t36 only works with Teensy 3.6 or Teensy 4.x.  Please select it in Tools > Boards"

I would appreciate the information :)

Best regards.
 
Hence my interest in this project as it is quite fresh.
However, using Teensy 3.6 is quite a waste of resources (not only financial)

Is it possible to somehow implement the USB host in earlier versions of Teensy?
 
You can add a USB Host Shield to any Teensy, even Teensy 2.0. But the performance is low and most of them lack current limit power (so hot plugging USB devices reboots your microcontroller) and the cost ends up being higher than just getting a Teensy 4.1 which has USB host and proper current limit USB host power built in.
 
Yes, I have noticed that the cost of such a "combined" kit is equal or even greater.

Well... it didn't hurt to ask. At least I know what and how :)

Thanks for your time.

Regards,
PeBe
 
USB was designed to minimize the cost of devices (peripherals) by putting all the complexity of managing communication in the host side. Traditionally only computers were USB hosts.

If you're cost-conscious, it's easy to feel like everything USB should be cheap. But the overall design was always to keep peripherals affordable with the assumption only "large" things like PC computers would be USB hosts.

That's why you tend to only see USB host as a feature on higher end microcontrollers. USB was only designed to make devices cheap.
 
Back
Top