Skip to content

a5677746shdh/miniTOTP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

miniTOTP

English | 简体中文

An offline TOTP authenticator (2FA) for the Waveshare ESP32-S3-Touch-ePaper-1.54 V2.

The device shows rolling 6-digit codes on a 1.54″ e-paper display. Long-press an account row to either speak the code (Chinese digits) or type it into a host over a Bluetooth HID keyboard, swipe to change pages, and sync time over Wi-Fi only when you ask. Secrets can be stored AES-CBC encrypted in config.yaml; the decryption key lives only in firmware (crypto.yaml).


Features

  • TOTP: HMAC-SHA1 (RFC 6238), 30 s period, 6 digits, up to 12 accounts (3 per page).
  • Optional secret encryption: config.yaml holds Base64(IV‖ciphertext); data/crypto.yaml is compile-time embedded only.
  • E-paper UI: 5×7 font; 6 px vertical progress bar (updates every 3 s); page dots; partial refresh with periodic full refresh.
  • Touch (FT6336): swipe paging, swipe-left for Info / swipe-right for Bluetooth, long-press to speak/type, tap/long-press NTP sync, tap/long-press Bluetooth connect/pair.
  • Bluetooth HID keyboard: pairs to a host as a BLE keyboard and types the code on long-press; mode is read / type / auto (type when connected, else speak); turns off on sleep / power-off / low-refresh.
  • Speech: embedded 0–9 PCM via ES8311; volume, speed, gap configurable.
  • UI sound effects: distinct tap / long-press / swipe tones, volume configurable.
  • On-demand NTP: Wi-Fi STA → SNTP → PCF85063 RTC → disconnect.
  • Power: auto deep sleep, low-refresh idle mode, PWR long-press cuts VBAT; configurable BOOT short/long actions (full-refresh / Bluetooth connect / wake-hold / Bluetooth pair), with per-item wake-hold ignore.
  • Config: YAML; load order SD card → SPIFFS → built-in defaults.

Hardware

Target board: ESP32-S3-Touch-ePaper-1.54 V2 (ESP32-S3-PICO-1-N8R8, 8 MB Flash / 8 MB PSRAM).

Peripheral Chip Bus
E-paper SSD1681 200×200 SPI2
RTC PCF85063 @ 0x51 I2C0
Touch FT6336 @ 0x38 I2C0
Audio ES8311 I2S + I2C0
Storage microSD 1-bit SDMMC

Pin map: main/user_config.h. Details: HARDWARE.md.

Non-touch boards run with touch disabled (no paging, speech, or manual sync).


Build and flash

Requirements

. ~/esp/esp-idf/export.sh
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

Use sdkconfig.defaults for FatFS LFN (required for /sdcard/miniTOTP/config.yaml). Prefer idf.py build over repeated set-target.

macOS: ./flash.sh or ./flash.sh --monitor

Before build:

cp data/crypto.yaml.example data/crypto.yaml
# Edit algorithm / key_hex

Build embeds data/config.yaml (SPIFFS), data/crypto.yaml, and data/audio/*.pcm.


Web simulator

python3 sim_server.py
# Open http://localhost:8765

Mirrors the firmware: progress bar, page dots, Info/Bluetooth screens (two-line MAC, status, connect/pair button), sync wait blink, and UI sound effects. Double-click the canvas to simulate a long-press (speak/type); the text box under the canvas shows what would be typed over Bluetooth. See simulator.html for the full layout (dual canvas, crypto tools, config editor).


Configuration

Load order

  1. /sdcard/miniTOTP/config.yaml (case variants; needs FatFS LFN)
  2. /spiffs/config.yaml (from data/config.yaml at flash time)
  3. Built-in defaults in main/config.c

The repo data/config.yaml is a demo template (e.g. sleep_timeout_sec: 600) and may differ from firmware fallbacks (e.g. 60).

Example YAML (firmware fallback defaults)

wifi_networks:
  - ssid: home
    pass: your_password
ntp_server: pool.ntp.org
timezone_offset: 8
auto_sleep_enable: 1
sleep_timeout_sec: 60
low_refresh_enable: 1
low_refresh_timeout_sec: 120
# Full refresh after every N partial refreshes; 0=off, 1=every time, 2=every 2nd...
full_refresh_every_n: 10
# BOOT short: none | full_refresh | bluetooth
boot_short_action: full_refresh
# BOOT long (~1s): none | wake_hold | bluetooth_pair
boot_long_action: wake_hold
# Per-item ignore while wake-hold is active (each 0/1)
boot_wake_hold_ignore:
  sleep: 1
  low_refresh: 1
  bluetooth: 0
# --- Bluetooth HID keyboard ---
# bluetooth_enable: disabled | enabled (on, no auto-advertise) | auto (advertise at boot)
bluetooth_enable: enabled
bluetooth_name: miniTOTP
# --- Speech ---
audio_volume: 80
audio_speed: 100         # 50-200, 100 = normal
audio_gap_ms: 120        # gap between digits, 0 = continuous
# --- UI sound effects ---
sfx_volume: 60
# --- Account long-press: read | type (Bluetooth) | auto (type if connected) ---
account_long_press: auto
type_key_interval_ms: 80   # per-key delay when typing over Bluetooth
batt_v_empty: 3.0
batt_v_full: 4.20
display_rotate: 0

accounts:          # up to 12, 3 per page, swipe to page
  - name: GitHub
    secret: JBSWY3DPEHPK3PXP    # plain Base32
  - name: Work
    secret_kind: encrypted      # optional: Base64(IV‖ciphertext), needs crypto.yaml
    secret: "Base64..."

Up to 8 Wi-Fi networks; Info sync tries them in order. crypto.yaml is not read from SD/SPIFFS.

Encrypt on host:

python3 tools/encrypt_secret.py --algorithm aes-256-cbc \
  --key-hex <same as crypto.yaml> --plaintext JBSWY3DPEHPK3PXP

Controls

Action Effect
Swipe up/down (main) Change page
Swipe left (main) Info screen
Swipe right (main) Bluetooth screen
Swipe left/right (Info / Bluetooth) Back to main
Long-press account Speak code / type over Bluetooth (per account_long_press)
Tap/long-press Sync (Info) Wi-Fi + NTP
Tap Bluetooth button Enable / disconnect & disable Bluetooth
Long-press Bluetooth button Enter pairing (blinking indicator)
PWR short Deep sleep / wake (GPIO18 ext1)
PWR long (~1 s) Power off (VBAT cut)
BOOT short boot_short_action (default full refresh)
BOOT long boot_long_action (default wake hold)
Touch / PWR Exit low-refresh mode

First use: flash config + crypto → Info sync → offline.


Troubleshooting

Symptom Fix
SD config not loaded Enable CONFIG_FATFS_LFN_HEAP; check miniTOTP/config.yaml path
Encrypted account shows 000 000 Rebuild with valid data/crypto.yaml; matching key_hex
Codes wrong after sync Re-sync on Info; check Wi-Fi / ntp_server
Cannot wake after USB power-off Update firmware (PWR ext1 wake on shutdown path)

Project layout

miniTOTP/
├── main/                   # Firmware (see module headers for API docs)
│   ├── bt_hid/             # BLE HID keyboard profile + wrapper
│   └── audio/              # Embedded digit PCM (d0..d9) + SFX
├── data/                   # config.yaml (SPIFFS), crypto.yaml (firmware-embedded)
├── scripts/gen_digit_audio.sh   # Regenerate digit speech (macOS `say`)
├── tools/encrypt_secret.py      # Host-side AES secret encryption
├── README.md / README.zh-CN.md
├── CHANGELOG.md / CHANGELOG.zh-CN.md
├── HARDWARE.md / HARDWARE.zh-CN.md
├── simulator.html          # Web simulator
└── flash.sh

Implementation notes

  • TOTP uses UTC from rtc_get_unix_cached(); timezone_offset is display-only.
  • RTC cache ~250 ms; forced refresh near TOTP period edges.
  • Deep sleep wake: PWR GPIO18 ext1 (not BOOT).
  • E-paper: full baseline → partial updates → full LUT refresh every full_refresh_every_n top-bar redraws (0 = off).
  • Bluetooth security: only pairing accepts non-whitelisted connections; after bonding it re-advertises whitelist-only so the bonded host keeps exclusivity. Turns off on sleep / power-off / low-refresh unless ignored by wake-hold.

Changelog

See CHANGELOG.md.


License

Third-party components (e.g. esp_codec_dev) use their own licenses.

About

Portable password manager application running on Waveshare ESP32-S3-ePaper-1.54 V2

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages