Show Source

This started out as my attempt to reconstruct, from existing open code bases and through testing of a unit in hand, the TeleSensory ScreenPower PowerBraille serial wire protocol. While I had feared that, as with so many historic devices, documentation had been lost, Freedom Scientific was willing to provide me with their engineering document about the protocol! That’s amazing. Unfortunately, this document appears to be incomplete, or perhaps just describing an earlier version of the protocol than that spoken by the unit I have. Thus, some additional probing has been required.

In the interest of having an available public reference, this page represents, to the best of my knowledge and ability, enough to write a functional driver for a PowerBraille 80. This protocol is nominally shared by several other devices, but with per-device nuance that I cannot verify and so do not attempt to reproduce here. Please understand that there may be mistakes in the following and it is still mostly the result of educated guesses. There are no warranties associated with this text, not even implied ones.

The Manual

The user manual for this device is still quietly accessible at https://support.freedomscientific.com/Content/Documents/Manuals/Legacy/PowerBraille/PB_User_Manual.pdf but it is of limited utility for understanding the device at a software level.

Notably, the manual does not even document the acceptable voltage ranges for DC power input, and The Internet as a whole also seems to have forgotten. There appears to be some suggestion that 9V or 12V is the intended answer. I originally ran mine on 5V to be safe, but have recently supplied 12V instead with no ill effects. Some limited REing of the PSU circuitry on the board (in the fullness of time, I mean to capture the whole schematic) suggests a fairly wide range of acceptable inputs.

Despite that the PB80 does not claim to offer battery support, the circuitry is absolutely there (and so is the battery holder!), and there will be periodic reports of low battery in the serial stream. So much for documentation, eh?

Existing Open-Source Drivers

There are (were) several open-source accessibility solutions that speak (spoke) something claiming to be TeleSensory’s protocol, but how much of it is RE’d vs. as described in documentation I don’t know. Anyway, because they are useful references, I’ve included them here:

Messages From The Device

Structured device messages are variable length, with the most-significant three bits of the first byte determining the type and nature of any following bytes:

000 Miscellaneous reporting; see Low Battery Notice, Device Identification Message, and Cursor Routing Buttons.
100 Never observed

The remaining 6 values are all used for buttons.

Non-Cursor Buttons

The non-cursor-routing buttons are reported as a series of 5-bit bitmaps. Keys are reported on key-up or by the typematic mechanism’s timeout, but note that multiple key presses, if not sufficiently simultaneous for the device’s preference, may cancel typematic timeouts. Multiple keys down during any reporting period will be reported by logical OR. There will be no reports asserting that all non-cursor buttons are released; that is, of the six messages detailed below, at least one in any batch thereof will contain a set key bit.

The non-cursor buttons come in several flavors:

  • Two large buttons on the front, one convex (on the left, CVX) and one concave (on the right, CCV).
  • Small rocker switches on the front panel. Two on the left and two on the right. Each can be momentarily pushed into one of two positions, up or down. Left-to-right, we denote these buttons as F0D, F0U, F1D, F1U, …, F3U.
  • Two long rocker bars on the front panel, also with momentary up and down positions. The short (FSD, FSU) is on the left while the long (FLD, FLU) is on the right.
  • Four small momentary buttons on the top; two on the left and two on the right. Left to right, we denote these T0 through T3.
  • Four long bar momentary buttons on the top; left to right, these are TL0 through TL3.
  • The engineering document mentions, but does not describe, a keyboard detection flag, noted KBD below. It has never been observed in testing, but I lack a real AT keyaboard port and keyboard both.

The bitmaps are coded as per the following table. The device reports in this somewhat curious order (the leading triplets are 010, 110, 001, 101, 011, 111; this is counting up from 2 to 7 with the bits reversed), and the assignments to buttons seems somewhat haphazard.

  10000 01000 00100 00010 00001
010   F1D F1U F0D F0U
110 KBD F3D F3U F2D F2U
001     TL3   TL2
101     T3   T2
011 CCV FLD TL1 FLU TL0
111 CVX FSD T1 FSU T0

Some drivers, confusingly, always read these messages two bytes at a time. Fortunately, the device appears to always send them as such, but it does not appear to be required by the protocol.

Cursor Routing Buttons

With each Braille cell there is a momentary push button. These are reported as a single multi-byte message:

  • 0x00 0x08 – message header.
  • A length byte (n)
  • n bytes of switch status, one bit per switch; 0 for up, 1 for down.

Nominally, for a PB80, there are 15 sensor bytes reported, with 4 for “vertical” cursor routing keys and 11 for “horizontal”, but only the horizontal ones carry any meaning, as this display has only one row. It appears that TeleSensory was ambitious!

Unlike the non-cursor buttons above, a report will be issued on every transition, so one can expect a terminal message with all bit positions 0 once the operator has released all keys.

Test Result Messages

After initiating a device self test (0x0B), the device should eventually report success with 0x00 0x06 or failure with 0x00 0x07.

Device Identification Message

Emitted in response to an identify command (0x0A; see below).

  • 0x00 0x05 – message header.
  • 1 byte cell count; for PB80, that’s 0x51 (the device has 81 cells, despite its name).
  • 1 byte dot count; either 6 or 8, one assumes; I’ve only seen 8.
  • A 4 byte version field.
  • A 4 byte checksum (of what?).

Low Battery Notice

  • The two byte sequence 0x00 0x01. As the PB80 is not intended to be run from batteries, it’s somewhat mystifying that this gets sent at all, but it does, by default. See 0x12 Set Low Battery Transmission Mode.

Unused Message Headers

There appears to be a curious gap in message headers, as 0x00 0x02, 0x00 0x03, and 0x00 0x04 are skipped and undocumented. Perhaps they were reserved for a future use that never came.

Similarly, 0x00 0x09 through 0x00 0xFF and 0x80 through 0x9F are seemingly unused.

Messages To The Device

Messages to the device always begin with two 0xFF bytes. A single byte command determines the type and length of the rest of the payload. So far, we believe:

Command Payload Len Interpretation
0x00 0 Ignored?
0x01 40 20-cell write to display?
0x02 80 40-cell write to display?
0x03 160 80-cell write to display?
0x04 Variable Write to display
0x05 1 Set UART parameter
0x06 8 Set Vibration Parameters
0x07 1 Set display off timer; 1 second per count
0x08 1 Display Size
0x09 0 Elicit debug report
0x0A 0 Elicit Device Identification Message
0x0B 0 Trigger cell test; see Test Result Messages
0x0C 0 Trigger full diagnostics loop; power cycle req’d!
0x0D 2 Set typematic rate parameters
0x0E 1 Set debounce time; 10ms/count
0x0F 1 Set Echo Mode
0x10 1 Keypad Setup Mode byte
0x11 1 Set Active Display Mode
0x12 1 Set Low Battery Tx Mode
0x13 1 Unknown; uncorrelated with 0x09
0x14 3 Cursor Status
0x15 1 Unknown; reset? crash?
0x16 1 Unknown; uncorrelated with 0x09
0x17 0 Ignored?
0 Ignored?
0x30 0 Ignored?
? Unprobed
0x80 0 Ignored?
? Unprobed
0xF0 0 Ignored?
? Unprobed
0xFE 0 Ignored?
0xFF 0 Ignored?

Writes to Display

It is unclear why all of 0x01, 0x02, 0x03, 0x04 exist, especially with at least the last of these being variable length. Apparently 0x04 was added later and subsumed the earlier commands.

  • 0x01 is like 0x04 with the payload length byte set to 0x28 and the start position set to 0x00 and neither transmitted.
  • 0x02 is like 0x04 with the payload length byte set to 0x50 and the start position set to 0x00 and neither transmitted.
  • 0x03 is like 0x04 with the payload length byte set to 0xA0 and the start position set to 0x00 and neither transmitted.

0x04 Write

This variable-length Write command takes the following payload structure:

  • Mode byte; the logical OR of the following values:

    0x01 Cursor display (1 = shown, 0 = hidden)
    0x02 Enable first vibration parameters in cells
    0x04 Enable second vibration parameters in cells
    0x08 Enable third vibration parameters in cells
    0x10 Enable fourth vibration parameters in cells

    Bits 0xE0 appear ignored.

  • Cursor position column (0 = leftmost, 1 its right neighbor, etc.); values beyond the number of cells on the display cause the cursor to appear hidden.

  • Cursor type byte. It appears that only the least significant nibble matters:

    0 All dots up
    1 Cursor status configuration
    2 All dots but 1 up, dot 1 vibrating using first vibration parameters
    3 Dots 6 and 7 up
    n All dots up
  • payload length byte (n) in bytes of attr/data byte pairs field

  • start position (0 = leftmost, 1 = its right neighbor, etc.)

  • attr/data byte pairs. Attribute byte first.

Braille dots are encoded using bit…

0 3
1 4
2 5
6 7

The attribute byte is one of the following; other values have not yet been probed. Only 0x00, 0x02, and 0x04 are specified in the manual; 0x06 and 0x08 were found by experimentation and appear intended, while 0x0A, 0x0C, and 0x0E appear to just happen to work the way they do.

0x00 Character steady on
0x02 Character using first vibration parameters, if enabled in mode
0x04 Character using second vibration parameters, if enabled in mode
0x06 Character using third vibration parameters, if enabled in mode
0x08 Character using fourth vibration parameters, if enabled in mode
0x0A
0x0C
0x0E

Attribute bits 0xF1 appear to be ignored.

It is unclear what happens if one specifies an odd number of bytes for a field composed of pairs… probably unwise.

0x05 UART Parameters

0x05 takes a 1-byte payload that appears to configure the UART.

0 RTS/CTS off, both UARTs
1 RTS/CTS on, both UARTs
2 Set current UART 4800 baud
3 Set current UART 9600 baud
4 Set current UART 19200 baud

On power-up, the device defaults to 9600 baud on UART 1, 4800 baud on UART 2, and with RTS/CTS disabled on both UARTs. It seems that 8n1 is always expected.

It does not appear to be possible to change the baud rate of the UART other than the one targeted, in something of an odd contrast to Echo Mode.

0x06 Vibration Parameters

There are four vibration sets on the PB65/81. Each defines a square wave using two parameters: the on duration and off duration, so the 0x06 command naturally takes eight bytes of payload: 1-On 1-Off 2-On ... 4-Off. Each counter has units of centiseconds, and is rounded to the nearest multiple of 5. Zero (and so byte values 0, 1, and 2) is to be interpreted as 0x104 (that is, 2.6 seconds) rather than as no time.

The default setting is 0x0A 0x05 0x23 0x0A 0x41 0x0A 0x5F 0x0A. That is:

Mode Period (msec) Duty cycle
1 150 2/3
2 450 7/8
3 750 13/15
4 1050 19/21

0x08 Display Size

This somewhat erroneously named command can be used to split the display, displaying both UART’s screens. The argument is the size of UART 1’s portion of the screen, and is on the left.

0x09 Debug Report

Upon receipt of the 0x09 command byte, my unit reports something like

TELESENSORY BRAILLE 65/81 STATUS REPORT
BRAILLE 65/81 FIRMWARE, VERSION 1.0A, JUNE 22, 1995, BATTERY USAGE 07:00:07
NUMBER OF DISPLAY = 51H
DISPLAY SIZE = 51H
NUMBER OF DOTS = 8
DISPLAY OFF TIMER, 1SEC/CT = F0H
CURSOR STATUS UART 1 C_POS C_MODE UP_BIT ON_BIT VIB_BITS = 00H 00H FFH C0H 00H
CURSOR STATUS UART 2 C_POS C_MODE UP_BIT ON_BIT VIB_BITS = 00H 00H FFH C0H 00H
VIBRATION TIMERS, 10MS/CT ONX OFFX 1-4 = 0AH 05H 23H 0AH 41H 0AH 5FH 0AH
UART 1,2 HAND SHAKE RTS/CTS = OFF
UART 1 BAUD RATE = 9600
UART 2 BAUD RATE = 4800
ECHO MODE = 00H
ACTIVE DISPLAY MODE = 01H
KEYPAD SETUP, 10MS/CT DEBOUNSE START CONTINUE MODE = 02H 32H 0AH 03H
LOW BATTERY TRANSMISION MODE = 03H
CHECK SUM = 077EH
CHECK SUM = 077EH

There appear to be no framing bytes or anything around this, so programmatic use is unlikely to be all that easy. The second CHECK SUM line has appreciable delay after the = and before the result, so likely the unit is processing during that time.

0x0F Set Echo Mode

0x0 Disable echo mode
0x1 Bridge UART 1 to UART 2; UART 2 in command mode
0x2 Bridge UART 2 to UART 1; UART 1 in command mode
0x3 Bridge UARTs together; neither in command mode

When a UART is bridged, a BREAK condition will take it out of bridge mode and lower the corresponding bit (0x01 for UART 1, 0x02 for UART 2) of the bridge mode setting. Bridges are not interpreted by the PowerBraille device; that is, they are electrical, and so the sender must vary their baud rate to match the target of the device.

For sending a command from the terminal connected to UART 1 to a device connected to UART 2, for example, the terminal should…

  • Set echo mode to 0x01.
  • Adjust its baud rate to match that of the device.
  • Send bytes.
  • Place a BREAK condition on the bus.
  • Restore its baud rate to that of the PowerBraille.

If a response is expected, the likely procedure is a little more complex:

  • Set echo mode to 0x03.
  • Adjust its baud rate to match that of the device.
  • Transact with the device; the PowerBraille is entirely passive.
  • Place a BREAK condition on the bus; echo mode will be 0x02.
  • Restore its baud rate to that of the PowerBraille.
  • Restore the echo mode to 0x00.

0x10 Set Keypad Mode

Select which UARTs receive side and cursor keypress events. Bitwise OR of 0x01 for UART 1, 0x02 for UART 2. Default is 0x03. Other values silence reporting.

0x11 Set Active Display Mode

Mode 0x01 displays writes as received from UART 1, while 0x02 from UART 2.

Setting to any other value appears to have no effect; that is, the display behaves as though the mode were in its previous state.

0x12 Set Low Battery Transmission Mode

Select which UARTs receive low battery notifications. Bitwise OR of 0x01 for UART 1, 0x02 for UART 2. Default is 0x03.

0x14 Cursor Status

This command influences the behavior of cursor “type” 1. It takes three bytes:

  • The “up” dots: these dots are up if the underlying display has them up.
  • The “on” bits: these dots are forced up.
  • The “vibrating” bits: these dots are vibrating if they are up by either of the prior two mechanisms.

So, for example, with a cell displaying 0xA0, an up dots setting of 0xC0, an on dots setting of 0x18, and a vibrating setting of 0x0C, the cell has 0x90 always on and 0x08 vibrating.

The default setting is 0xFF up, 0xC0 on, and 0x00 vibrating. That is, the display unconditionally sets the bottom two dots and passes the six upper dots through (in fact it also passes the bottom two dots through, but they are then overridden).

Open Questions