-
Notifications
You must be signed in to change notification settings - Fork 5
Raw HID
Tutorial
HID devices usually do not require a device driver, which makes them very attractive. A user can plug in a HID compatible device at any time and it works without any additional software installation.
To exchange data with the host, all HID devices use so-called HID reports. Device developers are basically free to design the content of these reports as they wish. To enable the Host to find out how the data for a special device is structured, a HID device must provide a report descriptor which the Host can query if necessary. The HID report of a Teensy RAWHID device is a simple 64-byte data block, making data exchange extremely easy for both the host and the device application.
HID devices can not actively send data to the PC. Instead, they prepare a report and inform the host about it. As soon as the host finds time it will collect the report from a buffer in the device.
Using the Teensy RawHID usb mode is very easy. If, for example, you want to send data to the PC you simply fill a 64 byte buffer with your data and provide it for collecting by usb_rawhid_send(void* buffer, int timeout)
. The first parameter is the address of the 64 byte buffer, the second parameter is a timeout in ms. If the host didn't collect the report after the timeout the teensy will abort the operation.
Here a working example which sends an incrementing value in the first byte of the report. You can download this and the following examples from here: https://github.com/TeensyUser/doc/tree/master/examples/HID
uint8_t report[64];
void setup(){}
void loop()
{
report[0] = millis()/100;
usb_rawhid_send(report, 1000);
delay(100);
}
Receiving data on the PC is nearly as easy as sending it from the Teensy. However, it is important to understand that RAWHID exposes two HID interfaces, the first one is the actual RAWHID, the second one is a SEREMU interface. Operating systems (at least Win10, what about LINUX?) generate a dedicated HID device for each HID interface a device provides. Thus, if you plug in the Teeny two HID devices, one RAWHID and one SEREMU device will appear.
You can distinguish them by having a look at their usagepage and usages:
- RAWHID: usage page = 0xFFAB, usage: 0x0200
- SEREMU: usage page = 0xFFC9, usage: 0x0004
The following examples (Win10, c# and python) show how to read in those packages. The application looks for devices with the correct VID/PID and identifies the correct one by checking its usage identifier. If it finds the correct device it waits for HID Reports from the Teensy and prints out the first byte of the received report on the console.
DotNet C-Sharp
This example uses [Mike ò Brians hidlibrary](https://www.nuget.org/packages/hidlibrary/3.3.24) to talk to the HID devices.using HidLibrary;
using System;
using System.Linq;
namespace receiver
{
class Program
{
static void Main(string[] args)
{
var teensy = HidDevices.Enumerate(0x16C0, 0x0486) // 0x486 -> usb type raw hid
.Where(d => d.Capabilities.Usage==0x200) // usage 0x200 -> RawHID usage
.FirstOrDefault(); // take the first one we find on the USB bus
while (teensy != null)
{
var report = teensy.ReadReport(); // request a report from the teensy
Console.WriteLine(report.Data[0]); // and write the first byte on the console
}
}
}
}
Python
import hid
import keyboard
hidInterfaces = hid.enumerate() # get all hid interfaces
t_it = filter(lambda t: # filter by vid and usagepage/usage
t['vendor_id']==0x16c0 and
t['usage_page'] == 0xFFAB and
t['usage'] == 0x0200, hidInterfaces)
rawHID_interface = next(t_it, None) # 'firstOrDefault'
if rawHID_interface != None:
teensy = hid.Device(path = rawHID_interface["path"])
while not keyboard.is_pressed('esc'):
report = teensy.read(64) # wait for an incomming report...
print(report[0]) # ...and print first byte
The following examples demonstrate sending RAWHID reports from a PC to a Teensy. Instead of sending a boring, incrementing number, we send a random advice slip from adviceslip.com to the Teensy.
HID Sender DotNet C-Sharp
using HidLibrary;
using System;
using System.Json;
using System.Linq;
using System.Net;
namespace hid
{
class Program
{
static void Main(string[] args)
{
var teensy = HidDevices.Enumerate(0x16C0, 0x0486) // 0x486 -> usb type raw hid
.Where(d => d.Capabilities.Usage==0x200) // usage 0x200 -> RawHID usage
.FirstOrDefault(); // take the first one we find on the USB bus
if (teensy == null || !teensy.IsConnected) return;
for(int loops = 0; loops< 10; loops++) // don't read too much data from the web page...
{
String slip = new WebClient().DownloadString(@"https://api.adviceslip.com/advice"); // read a slip from adviceslip.com
String advice = JsonValue.Parse(slip)["slip"]["advice"]; // extract the advice from the json result
HidReport report = teensy.CreateReport(); // Generate an empty report
for(int i = 0; i< advice.Take(63).Count(); i++ ) // and copy the characters into it.
{ // limit to 63 bytes to ensure the report always has a EOS (\0) at the end
report.Data[i] = (byte) advice[i];
}
teensy.WriteReport(report); // send report to teensy
Console.WriteLine(advice); // echo on console
}
}
}
}
HID Sender Python
Python
import hid
from urllib import request
import json
hidInterfaces = hid.enumerate() # get all hid interfaces
t_it = filter(lambda t: # filter by vid and usagepage/usage
t['vendor_id'] == 0x16c0 and
t['usage_page'] == 0xFFAB and
t['usage'] == 0x0200, hidInterfaces)
rawHID_interface = next(t_it, None) # 'firstOrDefault'
if rawHID_interface != None:
teensy = hid.Device(path = rawHID_interface["path"])
for i in range(10):
jsonurl = request.urlopen("https://api.adviceslip.com/advice")
slip = '\0' + json.loads(jsonurl.read())['slip']['advice'] # the sent report must always start with \0
teensy.write(slip.encode()[:64]) # rawHID report is 64 bytes long
The receiving code on the Teensy is as simple as this:
void setup(){}
void loop()
{
char buf[64];
if(usb_rawhid_recv(buf, 10) > 0) // Wait for a received raw hid report
{
Serial.println(buf); // and print it on Serial (actually a SEREMU interface in this case)
}
}
And here the output which was written by the Teensy to the serial monitor (note that we limited the text length to 64 byte in this simple example)
Both, firmware and software can be downloaded from here.
- PJRC Raw HID page: https://www.pjrc.com/teensy/rawhid.html
- Forum posts:
Teensy is a PJRC trademark. Notes here are for reference and will typically refer to the ARM variants unless noted.