FOSS project for building a small offline desktop TOTP display using an Arduino-compatible board, a 1.8 inch SPI TFT, and a DS1302 real-time clock module.
AI assisted project.
The final device is intentionally simple:
- no Wi-Fi
- no Bluetooth
- no network stack
- no cloud dependency
- USB power only after setup
- TOTP generated locally from user-provided Base32 seeds
The main firmware is the DS1302 RTC-backed offline version:
firmware/rtc_ds1302_offline/RRSW_Offline_TOTP_DS1302_TFT.ino
A no-RTC fallback/lab version is also included:
firmware/no_rtc_fallback/RRSW_TOTP_NoRTC_TFT_NoFlicker.ino
This is not a secure element. It is a physically controlled desktop authenticator.
The Base32 seeds are stored in Arduino flash. Anyone with physical access and firmware-dump capability may be able to recover them. A future passcode or button lock can hide the screen from casual viewing, but it does not protect against firmware extraction.
Treat the flashed device like a password vault. Keep it physically controlled. Do not commit real secrets.
- Arduino Uno/Nano-compatible board, ideally with no Wi-Fi/Bluetooth
- LA048 SPI 128x160 v1.1 TFT or similar ST7735 1.8 inch display
- MH Real Time Clock Module 2 / DS1302 RTC
- Correct coin cell for the RTC module
- Breadboard/jumpers
- USB power outlet
Many inexpensive DS1302 RTC modules include a coin-cell charging path. If your module has that charging path, use a rechargeable LIR2032 cell.
Do not charge a disposable CR2032.
Only use a CR2032 if your exact module is designed for a non-rechargeable cell or you have removed/disabled the charging path.
Install the correct RTC battery before setting time for the final offline build.
| TFT pin | Arduino pin | Notes |
|---|---|---|
| LED | 3.3V, or D3 with PWM enabled | Backlight |
| SCK | D13 | SPI clock |
| SDA | D11 | SPI MOSI/data, not Arduino SDA/A4 |
| A0 | D9 | Display data/command, not Arduino analog A0 |
| RESET | D5 | Display reset |
| CS | D10 | Chip select |
| GND | GND | Common ground |
| VCC | 3.3V | Display logic power |
| DS1302 pin | Arduino pin |
|---|---|
| VCC | 5V |
| GND | GND |
| CLK | D6 |
| DAT | D7 |
| RST | D8 |
Keep all grounds common. Do not tie 5V and 3.3V rails together.
- Install Arduino libraries:
- Adafruit GFX Library
- Adafruit ST7735 and ST7789 Library
- Adafruit BusIO if prompted
- Wire TFT and RTC.
- Install the correct RTC battery.
- Export only selected accounts from Google Authenticator.
- Scan the export QR locally with
tools/qr_scanner/scan_ga_qr_webcam.py. - Decode it locally with
tools/ga_migration_decoder/ga_migration_to_arduino_tokens.py. - Paste selected token lines into the firmware
TokenConfig TOKENS[]section. - Flash the Arduino.
- Set RTC to UTC over Serial once.
- Run from a USB wall outlet offline.
Open Serial Monitor or PuTTY at:
9600 baud, 8 data bits, 1 stop bit, no parity, no flow control
Send current UTC time:
SET 2026-05-20 22:15:00
Verify:
TIME
Use the actual UTC time when setting it.
Safe to publish:
- hardware
- enclosure
- wiring
- test/demo token
- blurred codes
Do not publish:
- live OTPs
- Base32 secrets
- Google Authenticator export QRs
otpauth://orotpauth-migration://text- real firmware containing real token seeds
Expired OTP codes are not realistically reversible into the seed, but hiding codes and sensitive labels is still the safer public-posting habit.
A future six-button breadboard/perfboard add-on is planned primarily as a navigation and control pad:
- next page / previous page for larger token lists
- reveal / hide codes
- brightness up / down
- menu/select mode
- optional passcode gate before displaying codes
- panic blank screen
The passcode gate is only casual-view protection. It does not protect against firmware dumping because the Base32 TOTP seeds are stored in Arduino flash.
See docs/FUTURE_BUTTON_LOCK.md.
MIT. See LICENSE.