Skip to main content
ZeroKeyUSB replaces mechanical buttons with five copper touch pads connected to a dedicated TS06 capacitive controller. The firmware polls this controller over I²C and translates touches into navigation events.

Hardware overview

The controller handles baseline calibration internally and reports which channels are active via the status register.

Touch initialization

At startup (zerokey-setup.cpp), the firmware:
  1. Probes the TS06 at 0x69 with up to 5 retries (20 ms apart).
  2. If detected, writes minimum sensitivity (0x3F) to registers 0x00–0x02.
  3. Configures operation mode via registers 0x05–0x06.
  4. Sets ts06_ok = true — if the controller isn’t found, touch is disabled and the serial log shows "TS06 not found on I2C".

Polling cycle

The handleButtonChecker() method runs in the main loop:
  1. Reads STATUS_REGISTER (0x25) via readRegister() over I²C.
  2. For each channel bit:
    • Records pressStartTime when a channel first becomes active.
    • Applies a 80 ms debounce (DEBOUNCE_MS) — releases shorter than this are ignored.
    • Enforces a 150 ms channel lockout (CHANNEL_LOCKOUT_MS) — touching a different pad while one is active is ignored.
  3. On release:
    • If held > LONG_PRESS_THRESHOLD (800 ms) → dispatches long-press handler.
    • Otherwise → dispatches short-press handler.

Gesture mapping


Long-press visual feedback

When a long press is in progress, drawLongPressProgress() renders a filling progress bar on the OLED screen. This gives the user visual confirmation that they should keep holding. Releasing before 800 ms cancels the action.

PIN screen controls

On the PIN entry screen, the controls change:

Error handling

  • If the TS06 is not detected at boot, ts06_ok is set to false and the status register read returns 0x00 — effectively disabling touch input.
  • The firmware does not show a touch error screen; instead, the serial log reports the issue for debugging.
  • Touch is silently disabled during lockout delays (waitFromEeprom()) and during long-running operations like credential erasure.
The TS06 operates independently of EEPROM or ATECC608A operations. Since all share the same I²C bus at 100 kHz, touch polling is interleaved with other I²C traffic in the cooperative main loop.