ZW111 capacitive fingerprint sensor test

ZW111 pinout:

Note: make sure your connector follows same color sequence as this.

Datasheet: Download ZW111 datasheet


ZW111 to Classic ESP32 Connection:

Note: you must you 3.3v only. 5v may kill the module.

For this setup, we are powering both the touch sensor and the fingerprint sensor with 3.3V.

Pin Wire Color Function ESP32 Pin
1 White V_SENSOR (Touch Power) 3.3V
2 Blue TOUCH_OUT (Wake-up) GPIO 4
3 Green VCC (Module Power) 3.3V
4 Yellow UART_TX (Data Out) GPIO 16 (RX2)
5 Black UART_RX (Data In) GPIO 17 (TX2)
6 Red GND GND

ZW111 ESP32 Test Code:

Boot button single click: add new fingerprint id.
Boot button 3s press: erase all fingerprint id.
#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

// Hardware Serial 2 for ESP32
#define RX_PIN 16      
#define TX_PIN 17      
#define TOUCH_PIN 4
#define BOOT_BUTTON 0  

HardwareSerial mySerial(2);
Adafruit_Fingerprint finger(&mySerial);

int get_next_id() {
  for (int i = 1; i <= 40; i++) { // ZW111 supports 40 IDs
    if (finger.loadModel(i) != FINGERPRINT_OK) {
      return i;
    }
  }
  return -1; 
}

void enroll_finger() {
  int slot = get_next_id();
  if (slot == -1) {
    Serial.println("Storage full (40 IDs reached)!");
    return;
  }

  Serial.print("ENROLLING ID #");
  Serial.println(slot);

  Serial.println("Place finger...");
  while (finger.getImage() != FINGERPRINT_OK) { delay(50); }

  if (finger.image2Tz(1) != FINGERPRINT_OK) {
    Serial.println("Error: Image messy.");
    return;
  }

  Serial.println("Remove finger");
  delay(1000);
  while (finger.getImage() != FINGERPRINT_NOFINGER) { delay(50); }

  Serial.println("Place same finger again...");
  while (finger.getImage() != FINGERPRINT_OK) { delay(50); }

  if (finger.image2Tz(2) != FINGERPRINT_OK) {
    Serial.println("Error: Image messy.");
    return;
  }

  if (finger.createModel() == FINGERPRINT_OK) {
    if (finger.storeModel(slot) == FINGERPRINT_OK) {
      Serial.print("SUCCESS! Stored in ID ");
      Serial.println(slot);
    }
  } else {
    Serial.println("FAILED: Prints did not match.");
  }
}

void setup() {
  Serial.begin(115200);
  
  pinMode(TOUCH_PIN, INPUT_PULLDOWN);
  pinMode(BOOT_BUTTON, INPUT_PULLUP);

  // Baud rate 57600 is default for ZW111
  mySerial.begin(57600, SERIAL_8N1, RX_PIN, TX_PIN); 

  if (finger.verifyPassword()) {
    Serial.println("Found ZW111 Fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }
}

void loop() {
  // Scan when touch sensor is triggered
  if (digitalRead(TOUCH_PIN) == HIGH) {
    if (finger.getImage() == FINGERPRINT_OK) {
      if (finger.image2Tz(1) == FINGERPRINT_OK) {
        if (finger.fingerSearch() == FINGERPRINT_OK) {
          Serial.print("MATCH ID: ");
          Serial.print(finger.fingerID);
          Serial.print(" | Confidence: ");
          Serial.println(finger.confidence);
        } else {
          Serial.println("NOT RECOGNIZED");
        }
      }
    }
    while (digitalRead(TOUCH_PIN) == HIGH) { delay(50); }
  }

  // Use Boot button to enroll
  if (digitalRead(BOOT_BUTTON) == LOW) {
    delay(500); // debounce
    enroll_finger();
  }
}

Here is a test connection for ESP32 S3:

Pin Wire Color Function ESP32 S3 Pin
1 White V_SENSOR (Touch Power) 3.3V
2 Blue TOUCH_OUT (Wake-up) GPIO 4
3 Green VCC (Module Power) 3.3V
4 Yellow UART_TX (Data Out) GPIO 6
5 Black UART_RX (Data In) GPIO 7
6 Red GND GND


Now, here is the test code for ESP32 S3:
Boot button single click: add new fingerprint id.
Boot button 3s press: erase all fingerprint id.

S3 RGB:
BLUE = sensor connected successfully.
GREEN = fingerprint matched.
RED = fingerprint did not match .
PURPLE = all fingerprint id erased.
CYAN/LIGHT BLUE = first fingerprint capture take successful.
#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

#define RX_PIN 6      
#define TX_PIN 7      
#define TOUCH_PIN 4
#define BOOT_BUTTON 0  
#define RGB_BRIGHT 26 // 10% Brightness

HardwareSerial mySerial(2);
Adafruit_Fingerprint finger(&mySerial);

void setRGB(uint8_t r, uint8_t g, uint8_t b) {
  rgbLedWrite(RGB_BUILTIN, r, g, b);
}

void enroll_finger() {
  int slot = -1;
  for (int i = 1; i <= 40; i++) {
    if (finger.loadModel(i) != FINGERPRINT_OK) {
      slot = i;
      break;
    }
  }
  if (slot == -1) return;

  // First Take
  while (finger.getImage() != FINGERPRINT_OK) { delay(50); }
  if (finger.image2Tz(1) != FINGERPRINT_OK) return;
  
  setRGB(0, RGB_BRIGHT, RGB_BRIGHT); // Cyan: 1st Take Success
  Serial.println("Remove finger");
  delay(1000);
  while (finger.getImage() != FINGERPRINT_NOFINGER) { delay(50); }

  // Second Take
  while (finger.getImage() != FINGERPRINT_OK) { delay(50); }
  if (finger.image2Tz(2) != FINGERPRINT_OK) return;

  if (finger.createModel() == FINGERPRINT_OK && finger.storeModel(slot) == FINGERPRINT_OK) {
    setRGB(0, RGB_BRIGHT, 0); // Green: Full Success
    delay(1000);
  } else {
    setRGB(RGB_BRIGHT, 0, 0); // Red: Fail
    delay(1000);
  }
  setRGB(0, 0, RGB_BRIGHT); 
}

void erase_database() {
  if (finger.emptyDatabase() == FINGERPRINT_OK) {
    setRGB(RGB_BRIGHT, 0, RGB_BRIGHT); // Purple: Erase Success
    delay(2000);
  } else {
    setRGB(RGB_BRIGHT, 0, 0); 
    delay(1000);
  }
  setRGB(0, 0, RGB_BRIGHT);
}

void setup() {
  Serial.begin(115200);
  pinMode(TOUCH_PIN, INPUT_PULLDOWN);
  pinMode(BOOT_BUTTON, INPUT_PULLUP);
  mySerial.begin(57600, SERIAL_8N1, RX_PIN, TX_PIN); 

  if (finger.verifyPassword()) {
    setRGB(0, 0, RGB_BRIGHT); // Blue: Ready
  } else {
    while (1) { 
      setRGB(RGB_BRIGHT, 0, 0); 
      delay(200); setRGB(0, 0, 0); delay(200);
    }
  }
}

void loop() {
  if (digitalRead(TOUCH_PIN) == HIGH) {
    if (finger.getImage() == FINGERPRINT_OK && finger.image2Tz(1) == FINGERPRINT_OK) {
      if (finger.fingerSearch() == FINGERPRINT_OK) {
        setRGB(0, RGB_BRIGHT, 0); // Green
      } else {
        setRGB(RGB_BRIGHT, 0, 0); // Red
      }
    }
    delay(1000);
    setRGB(0, 0, RGB_BRIGHT);
    while (digitalRead(TOUCH_PIN) == HIGH) { delay(10); }
  }

  if (digitalRead(BOOT_BUTTON) == LOW) {
    unsigned long pressTime = millis();
    while (digitalRead(BOOT_BUTTON) == LOW) {
      if (millis() - pressTime > 3000) {
        erase_database();
        while (digitalRead(BOOT_BUTTON) == LOW);
        return;
      }
      delay(10);
    }
    enroll_finger();
  }
}

Comments