No announcement yet.

Unofficial TouchSense PR-1000 Protocol Description

  • Filter
  • Time
  • Show
Clear All
new posts

  • Unofficial TouchSense PR-1000 Protocol Description

    We have been selling this product for years. A users has made an amazing walk through document of the protocol that is used to program the PR-1000. Thanks egnor!

    Unofficial TouchSense PR-1000 Protocol Description

    The PR-1000 is a programmable rotary encoder with haptic feedback. What this means is that, in theory, it's the world's most awesome knob control. It can be instantly switched in software to be a free-spinning knob, a knob with "detents" (like a car's volume control), a knob with snappier fixed positions (like an old-style TV channel changer), variable resistance to motion, "set points" that it falls into (like the center point of a balance knob), and so on.
    As of this writing (June 2008), you can get one for $119.99 from It comes with a little USB interface board, the encoder itself, and a plastic knob that goes on the shaft (which you can replace with something sexier, if you like).
    The catch is that, for some dumb reason, Immersion aren't publishing the protocol for interacting with their hardware. You can buy an SDK from them for thousands of dollars. Not sure what's up with that, if I was selling hardware I would be shoving my SDK down people's throats, but anyway.
    The mp3Car guys have a cheesy Windows DLL that you can use to put it in one or two pre-programmed modes. That clearly won't suffice. So, with a bunch of screwing around, I have reverse engineered the protocol used to talk to the knob.
    Physical Interface

    The encoder itself has a little 4-pin connector. Looking at the connector with the key facing away, these are the pin assignments, from left to right:
    1. Receive Data (into encoder)
    2. Transmit Data (into encoder)
    3. +5V (draws about 70mA w/ magnet off)
    4. Ground
    The data lines use the RS-232 protocol but with 5V logic levels.
    If you're using the mp3Car kit, you don't need to worry about this; the mp3Car adapter board has a little FTDI USB/serial adapter chip, line level shifters and so on. For some reason this board is not a standard USB serial device, but you can get drivers:
    • For Windows, you can use mp3Car's official driver. It's apparently the same driver they use for some sort of XM radio interface, but don't worry about the name, it works fine, and the device shows up as COM5: or whatever.
    • For Mac, there are FTDI drivers you could probably adapt. You'll have to set the Vendor ID (0x6846) and Product ID (0x0001) somehow. You're on your own here.
    • I use Linux, which (as of 2.6) already includes a driver for the chip, but needs to be configured with the Vendor ID (0x6486) and Product ID (0x0001). Here's what I did (for Ubuntu Gutsy):
      echo options ftdi_sio vendor=0x6846 product=0x0001 > /etc/modprobe.d/pr1000
      echo ftdi_sio >> /etc/modules
      modprobe ftdi_sio
      Once that's done, the device shows up as /dev/ttyUSB0. (If you have other USB serial devices, it might be assigned a different name. Check your dmesg logs, I guess.)
    Anyway, after this is done, the knob should be on and you have a serial port device for talking to it. The knob uses 19200 baud, N/8/1, no flow control. You can use your favorite language and serial port library, I like Python and pySerial.
    Since there's no flow control, make sure not to send too much data all at once, it might overwhelm the device.
    Serial Interface

    The actual wire-level serial interface is really simple. The device has 60 byte-sized registers. Some of them are read-only, most are read-write. (Actually some of the registers are word-sized, but the high and low bytes are individually addressed.) Everything you do with the device is done by reading or writing these registers. Some of the registers reflect the current position of the knob, and when the user frobs it, you get updates.
    Here's the message format used for both directions (host->device and device->host):
    • Start byte: this is always hex 0x5A (90 decimal).
    • First register to read or write (one byte), plus hex 0x80 for writes.
    • Number of registers to read or write (one byte), plus 0x80 for writes (?).
    • For writes, the data for the registers to write (one byte each).
    • A checksum byte (XOR of the other bytes, including the start byte).
    For example, to get all 60 device registers, you would send 5A 00 3C 66. The device would respond with 5A 80 .... XX, where .... is a dump of all 60 register bytes, and XX is the appropriate checksum. If you wanted to set registers 13 and 14 to 1 and 2 you would send 5A 8D 82 01 02 56. (The device would not respond.)
    When the user wiggles the knob, the device sends 5A 86 05 ?? ?? ?? ?? ?? XX. This is giving you an update on registers 6-11 (the ?? values), which are the registers that change when the knob is moved, followed by the appropriate checksum (XX).
    Simple enough, right? The trick is knowing what all the registers do. Read on...
    Device Overview

    The PR-1000 is a rotary encoder coupled with an electromagnetic brake and a little microcontroller. The rotary encoder reports the precise position of the shaft at all times. The electromagnetic brake can be configured to provide varying amounts of "drag", which makes it easier or harder to turn the shaft. There's a limit to the amount of braking, which means that there's no such thing as a total hard stop on shaft rotation. If you configure it for a hard stop, you can always push past it with a little bit of force. (Don't worry, this doesn't hurt anything.) This also means that the device can only resist motion, it can't actually rotate the shaft under its own power, for example it can't "spring back" into a set position when the user lets go.
    What you're defining is a resistance profile which tells the device how much resistance to apply for which position of the knob. The microcontroller adjusts the brake as the user turns the knob to give the illusion of going over hills and valleys. One slightly fancy bit is that the resistance profile can be longer than one full turn of the knob, meaning it takes several turns to wind your way through it. (Think about a tuner control, where you rotate the knob many times to work your way from one end of the dial to another, but there are hard stops at the ends and perhaps notches along the way for stations.)
    You don't get to define a totally arbitrary resistance profile. The profile has a beginning, and an ending, and in between a specific number of sections (the term is mine, probably Immersion calls them something else). You get to define two types of sections, default sections and alternate sections. Every section of each type is identical, so you have a series of identical bumps or notches or whatever. Normally all sections are default sections, but you can specify that a certain subrange of sections are alternate sections, or that every Nth section is an alternate section. So, for a balance control, you could have 11 notches, but make the center one deeper or wider or whatever.
    For each type of section, you can choose the basic shape, which can either be flat (constant resistance), one of two stock shapes (a hill and a notch), or a custom shape defined by a series of control points. You can also set the width and maximum resistance and various other attributes separately for each section type.
    For the endpoints, you can either make them hard stops (again, there's no true hard stop, the user can push through with some force), or a gentle hill, or no barrier at all (the wheel just keeps spinning).
    Okay, now you've got the basic idea, let's see the actual settings.

    Unless specified, read/write registers default to 0.
    Registers 0-3: fixed mystery values, read-only These registers are always fixed on my device, to 0x01, 0x07, 0x03, and 0x40 respectively. I'm guessing they're some sort of version or capability code.
    Registers 4,5: encoder resolution (lo, hi byte), read-only This two byte value gives the resolution of the encoder, in counts per revolution. On my device this is 0x880 (2176) and apparently can't be changed. That means the smallest movement it can detect is 0.17 degrees, which isn't bad.
    Register 6: push button, read-only This is 0 normally, 1 when the knob is pressed in. You will automatically get an update when the knob is pushed or released.
    Register 7: configuration version, read-only This value starts at 0 and increments by one any time you program one of the registers. You will automatically get an update when you set a register. You can use this value to know that the device has processed your update.
    Register 8: current section, read-write Which section the knob is currently positioned within. This can be from 1 to the number of sections defined (see register 11), -1 (0xFF) if the knob is before the first section, or -2 (0xFE) if the knob is after the last section. You will automatically get an update when the knob moves. You can set this value; the knob doesn't physically rotate, but it acts as if it had.
    Registers 9,10: position within section (lo, hi byte), read-write The exact position within this section, in encoder counts (see registers 4,5). The range of values depends on the width of the section (see registers 16,17 and 30,31), but is negative on the left side, 0 in the center of the section, and positive on the right side. You will automatically get an update when the knob moves. You can set this value; the knob doesn't physically rotate, but it acts as if it had.
    Register 11: number of sections, read-write, default 255 (0xFF) The number of sections (both default and alternate) from the beginning to the end. Note that there are two "half sections" added before and after the defined number of sections, which seem to be put there by Immersion to merge better with the end points, and also to make things confusing. So really you will end up with N+1 sections.
    Register 12: endpoint stops, read-write, default 17 (0x11) The high nybble controls the left endpoint, the low nybble controls the right endpoint. Each nybble can be one of these values:
    • 1 - no barrier at all (knob freely spins past the endpoint)
    • 2 - hard stop (knob stops suddenly at endpoint)
    • 3 - gentle hill (knob encounters resistance at endpoint)
    Registers 13,14: endpoint hill width (lo, hi byte), read-write For "hill" endpoints, the width of the hill in encoder counts (see registers 4,5).
    Register 15: endpoint hill maximum resistance, read-write For "hill" endpoints, the strength of maximum resistance (0 to 100?).
    Registers 16,17: default section width (lo, hi byte), read-write The width in encoder counts (see registers 4,5) of default sections. Does not include inter-section spacing, if any (see registers 28,29).
    Registers 18,19: default section shape size (lo, hi byte), read-write Normally set to the same as the section width (registers 16,17), this controls the size of the actual section shape in encoder counts (see registers 4,5). Smaller values make for "quicker" hills and valleys.
    Register 20: default section maximum resistance, read-write Set the maximum resistance used in the section (0 to 100?). Higher values make for "harder" hills and "deeper" valleys.
    Register 21: default section shape, read-write
    • 0 - constant drag force
    • 1 - gentle "bump" hill between sections
    • 2 - sudden "notch" in the middle of the section
    • 3 - custom shape (see registers 46-59)
    • 4 - some other kind of custom shape (???)
    Registers 22,23: default "snap" position (lo, hi byte), read-write These registers (along with registers 24,25) control some sort of "snap" effect I don't fully understand. They seem to set the position within the section where the "snap" is triggered.
    Register 24: default "snap" duration (?), read-write (???)
    Register 25: default "snap" resistance (?), read-write (???)
    Register 26: default section minimum resistance, read-write Set the base resistance (0-100?) that will always apply within the section, which is also the resistance used in inter-section spacing (see registers 28,29).
    Register 27: ??? Unknown/unused? Padding?
    Registers 28,29: inter-section spacing (lo, hi byte), read-write Create a blank spot between each section, with the specified width in encoder counts (see registers 4,5). The blank spot has the minimum resistance (see register 26).
    Registers 30-40: alternate section settings, read-write These registers are the same as registers 16-26, but they define the alternate section type instead of the default section type. See registers 42-45 for which sections are alternate sections. Note that the inter-section spacing does not apply to alternate sections.
    Register 42: alternate section subrange start, read-write Sections starting with this section use the alternate section settings (registers 30-40) instead of the default section settings (registers 16-26). This can be from 1 to the number of sections defined (see register 11).
    Register 43: alternate section subrange length, read-write The number of sections in a row which use alternate settings instead of default settings. (See register 42.)
    Register 44: alternate section periodic start, read-write Make every Nth section (see register 45 for the value of N) use alternate settings instead of default settings, starting with this section. This can be from 1 to the number of sections defined (see register 11).
    Register 45: alternate section periodic interval, read-write Define the repeat interval for settings which use the alternate section settings. For example, if set to 2, every other section will use the alternate settings. (Register 44 would control whether it is the even or odd sections.)
    Registers 46-53: custom shape control points (lo, hi * 4) When the custom section shape is set (see register 21, or register 35 for alternate settings), these control points are used to define the shape. Each control point is a two-byte offset in encoder counts (see registers 4,5) from the center of the shape (positive or negative). See registers 54-59 for the actual resistance values at these points.
    Registers 54-59: custom shape resistance values For custom shape profiles, define the resistance values (0-100?) for the far left side of the shape, then for each of the custom control point positions (see registers 46-53), then for the far right side of the shape. Resistance values at other points are interpolated between the nearest control points.

    To set up a simple volume knob with detents and endpoints (this one goes to 11), send these bytes:
    5A 8B 82 0A 22 7B # 10 sections, hard-stop endpoints
    5A 90 86 00 80 00 80 32 01 7F # 128 counts wide, 50 resistance, hill bumps
    To set up a balance knob with a fixed range and a notch in the center, send these bytes:
    5A 8B 82 09 22 78 # 9 sections, hard-stop endpoints
    5A 90 86 00 80 00 80 32 00 7E # default section settings: constant resistance
    5A 9E 86 00 80 00 80 32 02 72 # alternate settings: a notch in the center
    5A AA 02 05 01 F6 # use alternate settings for the center section
    I haven't actually tested these examples, so, um, let me know if I goofed. Also in general please send me corrections or things you've figured out, in general this is all my best guess because I don't have any real docs.
    -- egnor at ofb dot net