Skip to main content

USB Host

TinyCLR's USB Host API lets a SITCore device act as the USB host — driving the bus and accepting USB devices that you'd normally plug into a PC. Supported device classes:

  • HID — keyboards, mice, joysticks
  • Mass Storage (MSC) — USB sticks, external drives (FAT16/FAT32, see File System)
  • Raw devices — anything else, accessed via the underlying USB descriptors and pipes

NuGet packages: GHIElectronics.TinyCLR.Devices.UsbHost, plus GHIElectronics.TinyCLR.Devices.Storage and GHIElectronics.TinyCLR.IO if you need mass-storage support.

note

USB hubs aren't supported. Devices with multiple interfaces (composite devices) or built-in hubs (some USB sticks) won't enumerate correctly.

Detecting connections

The UsbHostController raises a single OnConnectionChangedEvent for every connect, disconnect, and bad-device condition. Inspect e.Type to find out what was plugged in and instantiate the matching driver class.

using System.Diagnostics;
using System.IO;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Storage;
using GHIElectronics.TinyCLR.Devices.UsbHost;
using GHIElectronics.TinyCLR.Devices.UsbHost.Descriptors;
using GHIElectronics.TinyCLR.Pins;
using GHIElectronics.TinyCLR.IO;

var usbHostController = UsbHostController.GetDefault();
usbHostController.OnConnectionChangedEvent += OnConnectionChanged;
usbHostController.Enable();

Thread.Sleep(Timeout.Infinite);

void OnConnectionChanged(UsbHostController sender, DeviceConnectionEventArgs e){
Debug.WriteLine("Id=" + e.Id + " interface=" + e.InterfaceIndex + " port=" + e.PortNumber
+ " type=" + e.Type + " vid=" + e.VendorId + " pid=" + e.ProductId);

switch (e.DeviceStatus){
case DeviceConnectionStatus.Connected:
HandleConnect(e);
break;
case DeviceConnectionStatus.Disconnected:
Debug.WriteLine("Device disconnected");
// Unmount file system here if previously mounted.
break;
case DeviceConnectionStatus.Bad:
Debug.WriteLine("Bad device");
break;
}
}

void HandleConnect(DeviceConnectionEventArgs e){
switch (e.Type){
case BaseDevice.DeviceType.Keyboard:
var keyboard = new Keyboard(e.Id, e.InterfaceIndex);
keyboard.KeyDown += (s, a) => Debug.WriteLine("Key down: " + a.Which + " (ASCII " + a.ASCII + ")");
keyboard.KeyUp += (s, a) => Debug.WriteLine("Key up: " + a.Which + " (ASCII " + a.ASCII + ")");
break;

case BaseDevice.DeviceType.Mouse:
var mouse = new Mouse(e.Id, e.InterfaceIndex);
mouse.CursorMoved += (s, a) => Debug.WriteLine("Mouse: " + a.NewPosition.X + "," + a.NewPosition.Y);
mouse.ButtonChanged += (s, a) => Debug.WriteLine("Mouse button: " + a.Which);
break;

case BaseDevice.DeviceType.Joystick:
var joystick = new Joystick(e.Id, e.InterfaceIndex);
joystick.CursorMoved += (s, a) => Debug.WriteLine("Joystick: " + a.NewPosition.X + "," + a.NewPosition.Y);
joystick.HatSwitchPressed += (s, a) => Debug.WriteLine("Joystick hat: " + a.Direction);
joystick.ButtonChanged += (s, a) => Debug.WriteLine("Joystick button: " + a.Which);
break;

case BaseDevice.DeviceType.MassStorage:
MountMassStorage();
break;

default:
ReadRawDevice(e);
break;
}
}

Mass Storage

When a USB stick or external drive is detected, mount it through StorageController and use it like any other file system:

void MountMassStorage()
{
var storage = StorageController.FromName(SC20260.StorageController.UsbHostMassStorage);
var drive = FileSystem.Mount(storage.Hdc);
var info = new DriveInfo(drive.Name);

Debug.WriteLine("Volume: " + info.VolumeLabel);
Debug.WriteLine("Format: " + info.DriveFormat);
Debug.WriteLine("Total size: " + info.TotalSize);
Debug.WriteLine("Free space: " + info.TotalFreeSpace);
Debug.WriteLine("Root: " + info.RootDirectory);
}

The mount lives until FileSystem.Unmount(...) (typically called on the Disconnected event).

Raw devices

If a device's class doesn't match Keyboard, Mouse, Joystick, or MassStorage, you can talk to it as a raw USB device — reading descriptors, opening pipes, and exchanging bytes directly. Useful for proprietary HID devices, custom hardware, or testing unusual USB peripherals.

void ReadRawDevice(DeviceConnectionEventArgs e)
{
var raw = new RawDevice(e.Id, e.InterfaceIndex, e.Type);

var deviceDescriptor = raw.GetDeviceDescriptor();
var configDescriptor = raw.GetConfigurationDescriptor(0);

// SET_CONFIGURATION(1) — standard setup packet to activate config 1.
raw.SendSetupPacket(0, 9, 1, 0);

// Build an Endpoint descriptor for an interrupt-in endpoint (address 0x81, 8-byte max packet, 10 ms poll).
var endpointData = new byte[7] {
7, // Descriptor length.
5, // Endpoint descriptor type.
0x81, // Input endpoint, address 1.
3, // Interrupt transfer type.
8, 0, // Max packet size = 8 (LSB, MSB).
10, // Polling interval (ms).
};

var endpoint = new Endpoint(endpointData, 0);
var pipe = raw.OpenPipe(endpoint);
pipe.TransferTimeout = 10;

var data = new byte[8];
var read = pipe.Transfer(data);

if (read > 0)
{
Debug.WriteLine("Raw device read " + read + " bytes: "
+ data[0] + "," + data[1] + "," + data[2] + "," + data[3]);
}
else
{
Debug.WriteLine("No data available");
}

Thread.Sleep(500);
}

The endpoint descriptor (7 bytes) is the standard USB endpoint descriptor format from the USB spec — fill in the values that match your device's interface, available in its USB documentation or via tools like lsusb -v on Linux.

API reference

NamespaceDescription
GHIElectronics.TinyCLR.Devices.UsbHostUSB host controller and device enumeration