Skip to content

Readout protocol

The readout board exposes a USB CDC serial interface; the nominal serial baud rate is ignored. Commands are text lines terminated by \n. Streaming records and command replies share the same connection, so a host must distinguish them by record type. The Python package implements this protocol in opentac.device; the specification below supports alternative host implementations.

Records from the board

The first comma-separated field identifies the record type.

F,<frame>,<gen>,<mode>,<dt_us>,<dropped>,<N values>,<IMU>
X,<frame>,<gen>,<mode>,<dt_us>,<dropped>,<n>,<mask hex>,<n values>,<IMU>
V,<seq>,<ax>,<ay>,<az>
M,<gen>,<mode>,<tac_en>,<vib_en>,<ori_en>,<active>,<shear>
R,...
# ...
Record Meaning
F a raster frame: one value per taxel, in manifest pixels order, 12-bit counts
X an Xampling frame: only the n taxels read. <mask hex> is ceil(N/8) bytes; bit k of byte j marks taxel 8j+k (LSB first). Values follow in taxel order, as counts above the board's zero.
V one accelerometer sample (mg), when VIB_ENABLE is 1
M status, once a second and on any change
R a reply to a command
# Human-readable diagnostic message

<IMU> is 13 fields: accelerometer (mg, 3), gyroscope (mdps, 3), quaternion (w, x, y, z) and Euler angles (roll, pitch, yaw, degrees). <gen> counts accepted setting changes since boot; if it goes down, the board restarted and its session settings are back at their defaults. <mode> is the TAC_MODE that made the frame. <dt_us> is the time since the previous frame.

A board with no sensor set up sends no F or X records.

Commands

Command Reply Operation
? R,CAPS,..., one R,PARAM per parameter, R,END capabilities and the parameter table
p <id> <value> R,PARAM,<id>,<name>,<lo>,<hi>,<value> set a parameter; the reply gives the value stored (clamped to range)
P [id] R,PARAM,... (all: then R,END) read one parameter, or all
G R,CFG,... or R,NOCFG the stored sensor config
S<len> R,OK,S,<len> start sending a config of len bytes; scanning pauses
D<offset>,<hex> R,OK,D,<offset>,<n> config bytes at an offset (opentac sends 64 per line)
E R,CFG,... check the config, write it to flash, resume
A R,OK,A abandon a config being sent
X R,OK,X erase the stored config
Z R,OK,Z,<frames>,<zero mean>,<zero max>,<noise median>,<noise max>,<noisy> measure the xampling zero; nothing may touch the sensor
t / z R,STAT,<frames>,<vib dropped>,<scan us max>,<loop us max>,<gen> / R,OK,Z statistics / reset them
c <r> <g> <b> R,OK,C LED colour, in LED_MODE manual
B R,OK,B reboot into the STM32 USB bootloader (DFU) for flashing

A rejected command returns R,ERR,<reason>. Reasons include NOBASE (xampling before Z), NOCFG (no sensor set up), NOTIMPL (mode not implemented), GAIN (gain switch did not answer), NOPARAM, SYNTAX, and FLASH,<stage>,<hal error>,<FLASH->SR>,<address>,<chip id>,<flash KB>. Match replies to commands by content, not by count: some commands answer with more than one line.

R,CAPS,<api>,<steps>,<channels>,<max taxels>,<modes hex>,<LED modes hex>,<params>,<modes needing shear hex>,<board>. <board> is READOUT60 or NODE16; the host picks that board's pad table from it.

R,CFG,<version>,<rows>,<cols>,<active>,<pitch x um>,<pitch y um>,<shear>,<id hex>,<crc hex>,<bytes>. <id> is the CRC-32 of the manifest file the config was made from.

Parameters

These parameters are session settings and return to their firmware defaults after a restart. The OpenTac host library overrides some defaults when opening a session, as indicated below.

id Name Range Default
2 TAC_MODE 0 raster, 1 xamp_fixed, 2 xamp_grow 0
3 LED_MODE 0 off, 1 manual, 3 heartbeat, 4 sensor 3
4 LED_BRIGHT 0..255 255
5 ROW_SETTLE_US 1..50 2
6 MUX_SETTLE_US 1..50 1 (opentac sets 8)
7 TAC_ENABLE 0/1 1
8 VIB_ENABLE 0/1 1 (0 on a bus node; opentac sets 0)
9 ORIENT_ENABLE 0/1 1 (0 on a bus node; opentac sets 0)
19 LED_RATE_HZ 1..100 1
24 FRAME_MAX_HZ 0..1000, 0 = free-running 0
25 VIB_ODR_HZ 15..1920 960
26 ORIENT_ODR_HZ 15..480 240
27 ACC_FS_G 2, 4, 8, 16 4
28 GYRO_FS_DPS 125..4000 2000
29 XAMP_M 1..1024 128
30 XAMP_THRESH 1..4095 30
31 XAMP_SEARCH_MIN 0..1024 30
32 XAMP_ZERO_FRAMES 16..1024 64
33 XAMP_BUMP_FILL 0/1 0
34 XAMP_CONFIRM_US 0..500 50
35 GAIN_SW 0..15, 60-pin board only 0

GAIN_SW bit k closes switch k+1 of the MAX14662 gain switch (I²C 0x4C, register 0x00). Each switch puts a resistor across the 10 kΩ feedback resistor: 4.7 kΩ, 1.1 kΩ, 270 Ω, 68 Ω.

Binary sensor configuration

The S/D/E transaction uploads the following little-endian structure, defined in firmware/opentac_readout/ot_devcfg.h:

Offset Size Field
0 4 magic OTC1
4 2 version (2)
6 1 rows
7 1 columns
8 4 pitch x, µm
12 4 pitch y, µm
16 2 active taxels
18 2 shear entries (0, or = active taxels)
20 4 config id: CRC-32 of the manifest file
24 4 payload length
28 row lines, column lines, row grid positions, column grid positions (1 byte each), the taxel mask (1 bit per row x column, LSB first), shear groups (2 bytes per taxel, 0xFFFF = none)
end 4 CRC-32 of everything before it

A line is 0-31 for a shift-register step (driven) or 32-63 for mux channel line-32 (read). The board validates the configuration before erasing flash; a rejected configuration leaves the existing one intact.

Bus controller

The Teensy bus controller speaks the same commands, addressed to a board:

Line Operation
@<n> <command> send a command to node n
N list nodes: R,NODES,<count>, R,NODE,<n>,<addr>,<uid>,<board>,<online>, R,END
? R,MCAPS,<bus version>,<max nodes>,<bus hz>, R,END

Lines from node n come back as <n>|<line>, and frames as <n>,<t_us>|F,... with t_us on the controller's clock, shared by every node. J,<n>,<uid>,<board>,<how> reports a node joining, L,<n> one lost.