| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
An independent firmware that makes a Seeed XIAO ESP32-S3 + ZW101 fingerprint sensor speak the immurok BLE protocol, so immurok's companion apps drive it unmodified.
It stands on two projects:
tinytouch's own firmware is a USB HID / PIV smart-card device. This one replaces that entirely with a BLE implementation of immurok's protocol, on the same physical build.
Touch the sensor to unlock your Mac or Windows PC. Hold for two seconds to lock it. It binds to two computers at once and hops between them on a dedicated fingerprint, and it carries an on-device SSH key vault and TOTP vault that sign only after a fingerprint.
Touch to unlock the Mac; hold for two seconds to lock it again.
This project is not affiliated with, endorsed by, supported by, or produced by immurok. It is an independent reimplementation by an unrelated person, for different hardware than immurok sells.
- immurok did not write this firmware and cannot support it. If something here breaks, it is my bug, not theirs — open an issue on this repository. Please do not file issues, feature requests or support questions on immurok's repositories, and please do not contact them about this project.
- Buying immurok's hardware is not the same as using this, and using this is not a substitute for their product. If you want the polished, supported experience, buy their device.
- The name "immurok" is used here only to state what protocol this speaks, which is a factual description of compatibility — not a claim of origin, endorsement or partnership.
- No immurok firmware code is used. Their firmware is BSL-licensed and targets entirely different silicon. See Licensing and provenance.
immurok were asked before this was published and were happy for it to exist. That is a courtesy they extended, not an endorsement of the code.
Working and in daily use, on one person's desk, on one build of the hardware. Pairing, enrolment, matching, screen unlock, hold-to-lock, dual-host binding, host switching, the SSH vault (verified against a real ssh-add -L) and the TOTP vault all function against immurok's macOS app.
It has not been security-audited, has not been tested across ZW101 revisions, and has run on exactly one device. Read Security model before trusting it with anything that matters.
Identical to a tinytouch build — if you have already built one, this firmware runs on it as-is with no rewiring.
| Part | Used here | Notes |
|---|---|---|
| Microcontroller | Seeed Studio XIAO ESP32-S3 | Needs native USB and a hardware UART. Any ESP32-S3 board exposing those pins should work |
| Fingerprint sensor | ZW101-style UART sensor | Round capacitive sensor with an RGB "aura" ring, 0xEF01 packet protocol. Other sensors speaking the same protocol may work |
| Case | Printed top + bottom | hardware/case/, from tinytouch (MIT) |
| Wiring | 5 × silicone hookup wire | The sensor pigtail is short; see the case notes |
| Host | macOS (tested), Windows / Linux (untested) | Needs the matching immurok companion app |
The ESP32-S3 part sold as 8 MB flash is what this was built on, but the firmware targets a 4 MB layout so it fits either.
The ZW101 talks over UART1 at 57600 baud, 8N1.
| ZW101 wire | XIAO pin label | GPIO | Direction |
|---|---|---|---|
| TX | D7 | 44 | sensor → MCU (MCU RX) |
| RX | D6 | 43 | MCU → sensor (MCU TX) |
| IRQ / touch-out | D1 | 2 | sensor → MCU, active high, internal pull-down |
| VCC | 3V3 | — | 3.3 V only |
| GND | GND | — |
ZW101 XIAO ESP32-S3 ┌────────────┐ ┌───────────────┐ │ TX ──────┼────────────────┤ D7 / GPIO44 │ (MCU RX) │ RX ──────┼────────────────┤ D6 / GPIO43 │ (MCU TX) │ IRQ ──────┼────────────────┤ D1 / GPIO2 │ (active high) │ VCC ──────┼────────────────┤ 3V3 │ │ GND ──────┼────────────────┤ GND │ └────────────┘ └───────────────┘
Notes from building this:
Pin definitions live at the top of main/fingerprint.c if you need to move anything.
If you just want a working device and don't intend to modify the firmware, grab the latest release and flash the merged image. You need Python and esptool, nothing else:
pip install esptool
# One file, flashed at offset 0 — contains bootloader + partition table + app.
esptool.py --chip esp32s3 write_flash 0x0 immurok-esp32-vX.Y.Z-merged.binAdd -p PORT if it can't find the board (/dev/cu.usbmodem101, /dev/ttyACM0, COM7 — see the port table below). If flashing fails, hold BOOT, tap RESET, release BOOT, and retry.
Verify what you're flashing. Each release ships a SHA256SUMS file:
shasum -a 256 -c SHA256SUMSA prebuilt binary is a trust decision. You are running a security device on a stranger's compiled artifact. The checksums prove the file matches what I uploaded — they prove nothing about what I put in it. If that matters to you, and for a fingerprint authenticator it reasonably might, build from source instead using the instructions below. The build is reproducible from a clean checkout with ESP-IDF v5.3.2.
To erase everything and start fresh (this destroys pairing keys, fingerprints and vault contents): esptool.py --chip esp32s3 erase_flash
Written assuming you have never used ESP-IDF before. If you have, it is just idf.py set-target esp32s3 && idf.py flash monitor.
ESP-IDF is Espressif's SDK — the compiler, the libraries and the idf.py tool. This project is built and tested against v5.3.2; other 5.x releases will most likely work.
macOS / Linux
# Prerequisites: git, cmake, ninja, python3.
# macOS: brew install cmake ninja python3
# Debian/Ubuntu: sudo apt install git wget flex bison gperf python3 python3-venv \
# cmake ninja-build ccache libffi-dev libssl-dev dfu-util
mkdir -p ~/esp && cd ~/esp
git clone -b v5.3.2 --recursive https://github.com/espressif/esp-idf.git
cd ~/esp/esp-idf
./install.sh esp32s3Windows — use the ESP-IDF Windows Installer, pick v5.3.2, then use the "ESP-IDF PowerShell/CMD" shortcut it creates instead of running export.sh.
cd ~/esp
git clone https://github.com/nilava/immurok-esp32.git
cd immurok-esp32Every new terminal needs the ESP-IDF environment loaded first. This is the single most common stumble — if idf.py is "command not found", you skipped this line:
source ~/esp/esp-idf/export.sh # macOS/Linux, once per terminal session
idf.py set-target esp32s3 # only needed the first time
idf.py buildThe first build takes several minutes and prints a lot. It ends with Project build complete.
Plug the XIAO into USB and run:
idf.py flash monitoridf.py usually finds the board on its own. To be explicit, pass the port with -p:
| OS | Find the port | Looks like |
|---|---|---|
| macOS | ls /dev/cu.usbmodem* | /dev/cu.usbmodem101 |
| Linux | ls /dev/ttyACM* | /dev/ttyACM0 |
| Windows | Device Manager → Ports (COM & LPT) | COM7 |
idf.py -p /dev/cu.usbmodem101 flash monitormonitor shows the device's serial output. Press Ctrl+] to exit it (not Ctrl+C).
You should see something like:
fp: sensor verify OK with tx=43 rx=44 fp: sensor init: 0 template(s) enrolled, index bitmap=0x00 imk_crypto: crypto init: unpaired imk_service: advertising as immurok-tt (ok) immurok: immurok-esp32 boot; console: d=download r=restart ...
sensor verify OK means the wiring is right. If you instead see sensor verify failed on both orientations, the sensor is not answering — check the 3V3/GND connections first, then the two data wires.
The ring should settle to steady purple (or breathing red if no computer is paired yet). Now continue to Setup.
partitions.csv: 64 KB NVS at 0x9000, a single 3 MB app at 0x20000. There is no OTA slot — this firmware updates over USB. The NVS offset is deliberately kept stable across versions, so reflashing preserves your pairing keys, enrolled fingerprints and vault contents. idf.py erase-flash wipes all of that and puts you back to unpaired.
This firmware is only half of the system. The host-side software is immurok's, and it does the genuinely hard part — the PAM module, the Windows credential provider, the SSH agent, the secret vault. Get it from them, not from me:
| Platform | Repository | Tested with this firmware? |
|---|---|---|
| macOS | immurok/app-macos | Yes — this is what everything was developed against |
| Windows | immurok/app-win | Not tested. Same protocol, so expected to work — treat that as an expectation, not a result |
| Linux | immurok/app-linux-rs | Not tested. (Note: app-linux is archived; app-linux-rs is the current one) |
| AI agent skill | immurok/imk-skill | Not tested |
Their main project, docs and hardware: immurok/immurok · immurok/hardware · immurok/firmware · immurok.com
Again: support questions about those apps go to immurok; support questions about this firmware come to me. Please keep the two separate — it is unfair to land my bugs in their inbox.
To use the SSH vault, enable the SSH agent in the app, then ssh-add -L will list a key that lives only on the device. Screen-lock requests (hold-to-lock) require the app's opt-in "screen lock" setting to be turned on.
Every state the ring can be in, as it actually looks:
| Ring | State | When you see it |
|---|---|---|
![]() |
Steady purple | Ready and paired to a host |
![]() |
Breathing red | No computer reachable |
![]() |
Breathing purple | Reading your touch |
![]() |
Green | Recognised — the sensor's own indicator, see quirks |
![]() |
Steady red | Not recognised |
![]() |
Breathing cyan | Verify an enrolled finger to proceed (any gated action) |
![]() |
Breathing blue | Enrol: place finger — also shown while switching hosts |
![]() |
Steady cyan | Enrol: lift finger |
![]() |
Steady blue | Lock request sent (2-second hold) |
![]() |
Dark | Asleep — nothing for 3 minutes; any touch wakes it |
Only resting states sleep. Anything active or waiting — reading, enrolling, a gate armed for your finger, switching hosts — stays lit for as long as it takes.
Type these in idf.py monitor (USB-Serial-JTAG, no extra adapter needed):
| Key | Action |
|---|---|
| d | Reboot into ROM download mode (no BOOT/RESET button dance) |
| r | Restart |
| i | Sensor info: template count, index bitmap, per-slot load probe |
| w | Wipe all fingerprint templates |
| p | Dump both host binding slots |
| u | Clear both host bindings (fingerprints untouched) |
| c | Sweep the LED colour palette |
| s / S | Passive / active BLE scan of nearby advertisers |
Implemented opcodes: 0x01 GET_STATUS, 0x02 GET_BATT_RAW, 0x10/0x11 ENROLL START/CANCEL, 0x12 DELETE_FP, 0x13 FP_LIST, 0x22 FP_MATCH_ACK, 0x30-0x32 PAIR_INIT/CONFIRM/STATUS, 0x33 AUTH_REQUEST, 0x37 GATE_CANCEL, 0x38 CHALLENGE, 0x39 SLOT_STATUS, 0x3C SLOT_CLEAR, 0x60-0x69 the keystore range. Notifications: 0x21 signed match, 0x23 lock request.
Not implemented: immurok's OTA scheme. This firmware reports version 99.0.0 so the companion app's update checker always reads "up to date" and never offers an image this device could not apply. If you fork this, keep that in mind — a real version number will prompt users to flash immurok firmware onto incompatible hardware.
Please read this before relying on the device.
What it protects against. A remote attacker, or someone who picks up your unlocked laptop. Each touch is signed with a per-host key established by ECDH, so a replayed or forged match notification is rejected. SSH private keys never leave the device and are masked from protocol reads; signing requires a live fingerprint.
What it does not protect against.
Design rule worth preserving if you extend this: proximity, presence and any similar signal may remove access but must never grant it. Anything a nearby device broadcasts can be spoofed by any other nearby device.
These cost real debugging time and are documented here so they cost you less. They were established empirically on one unit and confirmed against the Hi-Link "Fingerprint module user communication protocol" v1.1 datasheet where possible.
This firmware is original C written for the ESP-IDF. It contains no code from immurok's firmware, which is under the Business Source License and targets a different MCU (WCH CH592F) and a different sensor.
What it was built from:
immurok/app-macos (Apache-2.0) — read as the authoritative description of the wire protocol. The behaviours it expects are reimplemented here in C; no Swift was copied.
immurok's protocol documentation — note that the repository carrying it is under the Business Source License 1.1. This project's position is that implementing a wire protocol for interoperability is not a derivative of the licensed work.
immurok were contacted before this repository was made public, and confirmed they are happy for it to be published, on the understanding that it is unaffiliated and not a derivative of their BSL-licensed firmware. Thanks to them for the quick and generous reply.
zimengxiong/tinytouch (MIT) — the hardware design this build is a copy of, and the origin of the working ZW101 UART command sequences. The printed case in hardware/case/ is redistributed from it unmodified, under its MIT licence, with the original copyright notice retained in hardware/case/LICENSE.tinytouch. None of tinytouch's firmware C is used — this project's firmware was written from scratch for a different protocol.
anildash/dashtouch (MIT) — its LED diagnostics informed early work on the aura command, though this hardware ultimately behaves differently and the datasheet settled the details.
Gadgetbridge — the Ultrahuman BLE naming convention, used only by the optional scan diagnostic.
If immurok would like anything changed about the framing, attribution, or this project's existence at any point, please open an issue — that request will be honoured.
This project's own code is released under the MIT License (see LICENSE).
zimengxiong/tinytouch — the hardware combination, the case, and the first ZW101 driver that proved this sensor could be made to behave. This project is a different firmware on that same physical build, and would not exist without it.
immurok — for a protocol clean enough to reimplement and companion apps good enough to be worth reimplementing it for. The apps do the genuinely hard part on the host side: the PAM module, the credential provider, the SSH agent.
| Back | FazBrowse Home | New Git URL |