Featured Image - Interfacing ESP32 with R307 Fingerprint Sensor

Posted on

by

in

Interfacing ESP32 with R307 Fingerprint Sensor

Introduction

Do you need to include biometric security such as fingerprints in your Internet of Things (IoT) projects? I highly suggest using your ESP32 microcontrollers that run with the Arduino framework to make use of this R307 fingerprint sensor. If you want to add another layer of security to your projects then checking the identity of every person who wants to access your premises thru fingerprints is an excellent option.

If you want to see a video demo presentation of this project then please see below or watch it on my YouTube channel.

How does fingerprint scanning work?

Sample fingerprint

Every finger on our hands contains unique patterns of fingerprints that are essentially different from another person’s fingerprints. Our fingerprint contains ridges and valleys when closely look upon and it helps us in handling things as it improves the friction between our hands and the object.

The process by which fingerprint scanning works is by capturing these ridges and valleys and transforming them into a digital image. The images are then stored (enrolled) and compared thru sophisticated algorithms by measuring the distance between the ridges and valleys.

Fingerprint scanners may come as optical, capacitive, or ultrasonic in nature and they each have their own advantages. Optical scanner works by shining a bright light and capturing a digital image of your fingerprints. Capacitive scanners on the other hand measure the distance between the ridges of your fingerprint to create the overall layout of your fingerprints. The ultrasonic sensor captures your fingerprints by using high-frequency waves to capture your fingerprint layout. More details can be found here.

What is the R307 fingerprint scanner?

R307 Fingerprint Sensor

This sensor is the upgraded version of the old R305 sensor from the Hangzhou Grow Technology company which comes from China. It is a small subsystem that contains its own DSP processor, image processing, and image comparison modules in a compact package that is very much ideal for Internet of Things (IoT) project that requires biometric security.

It has built-in support for Serial or UART (Universal Asynchronous Receiver Transmitter) protocol to be used when communicating with Microcontrollers and it has a USB 2.0 output that can you can connect to your laptop or workstation. We will be using the serial or UART option in this post for us to communicate with our ESP32 microcontroller running the Arduino framework.

Below are the most important technical parameter of this sensor and the complete datasheet can be found here.

Operating VoltageDC 4.2V-6V
Working currentTypical: 50mA
Baud rate(9600*N)bps, N=1~12 (default N=6)
Image acquiring time<0.5s
Storage capacity1000
InterfaceUART(TTLlogical
level)/ USB2.0
Working environment Temp: -10℃- +40℃
RH: 20%-85%

How R307 communicates with ESP32 Microcontroller?

As mentioned above, the R307 communicates with any Microcontroller thru serial communication or UART as shown in the image below. The baud rate can be altered and can be set between 9600~115200bps.

The ESP32 has 3 hardware implemented UART pins combinations available for use and we will need to wire it wherein the TX and RX, and the RX and TX pins of both components are inter-connected in reverse.

How R307 Sensor communicates with ESP32

Parts/Components

The followings are the parts or components required to follow along with this post.

Disclosure: These are affiliate links and I will earn small commissions to support my site when you buy through these links.

Wiring/Schematic

Below is how the pins are assigned to your R307 fingerprint sensor. The colors of the wires may change from one vendor to the other but the sequence is always the same. We can power our R307 using the 3.3 volts by shorting the jumper above but for this post, we will just be using the 5V pin to power this component.

R307 Close Up Shot
R307 Pinout

We will be using the hardware serial UART 2 of our ESP32 and will follow the following schematic diagram.

ESP32 - R307 - Wiring - Schematic

Prerequisites

I am using the PlatformIO IDE extension in Visual Studio Code while developing this project but the code here is applicable also to an Arduino IDE. If you are not familiar with PlatformIO then please see my earlier post on how to use this.

Related Content:
PlatformIO Tutorial for Arduino Development

Code

The code for this project is available in my GitHub repository which you can download as a zip file or clone using Git.

git clone https://github.com/donskytech/platformio-projects.git
cd esp32-projects/esp32-r307-fingerprint

Arduino Fingerprint Sensor Library

I will use the Adafruit-Fingerprint-Sensor-Library to communicate between my ESP32 and the R307 fingerprint sensor.

If you are using the PlatformIO extension then you can search for it in the Library tab and add it to your project.

PlatformIO Add Adafruit Fingerprint Sensor Library

Or if you are using the Arduino IDE 2.0 then search for it in the Libraries tab and add it to your project also.

Arduino IDE Add Adafruit Fingerprint Sensor Library

The following code shown in the next sections where taken from the example folder of the library and I had them modified to work with the ESP32 microcontroller.

Enroll Fingerprint

Below is the code for our ESP32 microcontroller on how you could enroll your fingerprint and saved it to the R307 fingerprint sensor database.

#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

uint8_t id;
uint8_t getFingerprintEnroll();

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ; // For Yun/Leo/Micro/Zero/...
  delay(100);
  Serial.println("\n\nAdafruit Fingerprint sensor enrollment");

  // set the data rate for the sensor serial port
  finger.begin(57600);

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

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x"));
  Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x"));
  Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: "));
  Serial.println(finger.capacity);
  Serial.print(F("Security level: "));
  Serial.println(finger.security_level);
  Serial.print(F("Device address: "));
  Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: "));
  Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: "));
  Serial.println(finger.baud_rate);
}

uint8_t readnumber(void)
{
  uint8_t num = 0;

  while (num == 0)
  {
    while (!Serial.available())
      ;
    num = Serial.parseInt();
  }
  return num;
}

void loop() // run over and over again
{
  Serial.println("Ready to enroll a fingerprint!");
  Serial.println("Please type in the ID # (from 1 to 127) you want to save this finger as...");
  id = readnumber();
  if (id == 0)
  { // ID #0 not allowed, try again!
    return;
  }
  Serial.print("Enrolling ID #");
  Serial.println(id);

  while (!getFingerprintEnroll())
    ;
}

uint8_t getFingerprintEnroll()
{

  int p = -1;
  Serial.print("Waiting for valid finger to enroll as #");
  Serial.println(id);
  while (p != FINGERPRINT_OK)
  {
    p = finger.getImage();
    switch (p)
    {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(1);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  Serial.println("Remove finger");
  delay(2000);
  p = 0;
  while (p != FINGERPRINT_NOFINGER)
  {
    p = finger.getImage();
  }
  Serial.print("ID ");
  Serial.println(id);
  p = -1;
  Serial.println("Place same finger again");
  while (p != FINGERPRINT_OK)
  {
    p = finger.getImage();
    switch (p)
    {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.print(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(2);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK converted!
  Serial.print("Creating model for #");
  Serial.println(id);

  p = finger.createModel();
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Prints matched!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_ENROLLMISMATCH)
  {
    Serial.println("Fingerprints did not match");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  Serial.print("ID ");
  Serial.println(id);
  p = finger.storeModel(id);
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Stored!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_BADLOCATION)
  {
    Serial.println("Could not store in that location");
    return p;
  }
  else if (p == FINGERPRINT_FLASHERR)
  {
    Serial.println("Error writing to flash");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  return true;
}

Let us try exploring what each line of the code does.

#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

uint8_t id;
uint8_t getFingerprintEnroll();

Import the Arduino and the Adafruit fingerprint library and then we define the HardwareSerial port by using the UART 2. We then initialize an instance of a Adafruit_Fingerprint class by passing an instance of our HardwareSerial class.

Next, we define a global variable id that will identify our fingerprint and forward declare the function getFingerprintEnroll()

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ; // For Yun/Leo/Micro/Zero/...
  delay(100);
  Serial.println("\n\nAdafruit Fingerprint sensor enrollment");

  // set the data rate for the sensor serial port
  finger.begin(57600);

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

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x"));
  Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x"));
  Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: "));
  Serial.println(finger.capacity);
  Serial.print(F("Security level: "));
  Serial.println(finger.security_level);
  Serial.print(F("Device address: "));
  Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: "));
  Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: "));
  Serial.println(finger.baud_rate);
}

In the setup() function, we initialize our serial monitor and begin communicating with our R307 fingerprint sensor. If we are able to communicate from our ESP32 then we could retrieve some of the sensor parameters.

uint8_t readnumber(void)
{
  uint8_t num = 0;

  while (num == 0)
  {
    while (!Serial.available())
      ;
    num = Serial.parseInt();
  }
  return num;
}

void loop() // run over and over again
{
  Serial.println("Ready to enroll a fingerprint!");
  Serial.println("Please type in the ID # (from 1 to 127) you want to save this finger as...");
  id = readnumber();
  if (id == 0)
  { // ID #0 not allowed, try again!
    return;
  }
  Serial.print("Enrolling ID #");
  Serial.println(id);

  while (!getFingerprintEnroll())
    ;
}

uint8_t getFingerprintEnroll()
{

  int p = -1;
  Serial.print("Waiting for valid finger to enroll as #");
  Serial.println(id);
  while (p != FINGERPRINT_OK)
  {
    p = finger.getImage();
    switch (p)
    {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(1);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  Serial.println("Remove finger");
  delay(2000);
  p = 0;
  while (p != FINGERPRINT_NOFINGER)
  {
    p = finger.getImage();
  }
  Serial.print("ID ");
  Serial.println(id);
  p = -1;
  Serial.println("Place same finger again");
  while (p != FINGERPRINT_OK)
  {
    p = finger.getImage();
    switch (p)
    {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.print(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }

  // OK success!

  p = finger.image2Tz(2);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK converted!
  Serial.print("Creating model for #");
  Serial.println(id);

  p = finger.createModel();
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Prints matched!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_ENROLLMISMATCH)
  {
    Serial.println("Fingerprints did not match");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  Serial.print("ID ");
  Serial.println(id);
  p = finger.storeModel(id);
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Stored!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_BADLOCATION)
  {
    Serial.println("Could not store in that location");
    return p;
  }
  else if (p == FINGERPRINT_FLASHERR)
  {
    Serial.println("Error writing to flash");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  return true;
}

The whole sequence above is the code for our enroll sequence and it will read our R307 fingerprint by letting the user enter the id of the fingerprint and then scan the fingerprint and then it will ask for confirmation again to validate the initial scan. If the initial and the next scan match then it will be saved into the internal file system of our R307 fingerprint sensor.

Deploy this sketch into your ESP32 microcontroller then you would see the following prompt displayed on your Arduino IDE.

Arduino IDE - Scan Fingerprint

If you are using PlatformIO IDE then you would see the following on your terminal.

PlatformIO Enroll Fingerprint Sequence

You need to scan the same finger twice as it will do validation if they are the same finger. If successful then it will be saved into the R307 flash database.

Checking Fingerprint

Below is the code on how you can check your fingerprint and if it will match those fingerprints inside our R307 database.

The code is initially commented out so you can just copy it into your main.cpp file when you are using PlatformIO IDE or create a new sketch when you are using the Arduino IDE.

#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

uint8_t getFingerprintID();

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ; // For Yun/Leo/Micro/Zero/...
  delay(100);
  Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  delay(5);
  if (finger.verifyPassword())
  {
    Serial.println("Found fingerprint sensor!");
  }
  else
  {
    Serial.println("Did not find fingerprint sensor :(");
    while (1)
    {
      delay(1);
    }
  }

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x"));
  Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x"));
  Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: "));
  Serial.println(finger.capacity);
  Serial.print(F("Security level: "));
  Serial.println(finger.security_level);
  Serial.print(F("Device address: "));
  Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: "));
  Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: "));
  Serial.println(finger.baud_rate);

  finger.getTemplateCount();

  if (finger.templateCount == 0)
  {
    Serial.print("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
  }
  else
  {
    Serial.println("Waiting for valid finger...");
    Serial.print("Sensor contains ");
    Serial.print(finger.templateCount);
    Serial.println(" templates");
  }
}

void loop()
{
  getFingerprintID();
  delay(50); // don't ned to run this at full speed.
}

uint8_t getFingerprintID()
{
  uint8_t p = finger.getImage();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image taken");
    break;
  case FINGERPRINT_NOFINGER:
    Serial.println("No finger detected");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_IMAGEFAIL:
    Serial.println("Imaging error");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK converted!
  p = finger.fingerSearch();
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Found a print match!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_NOTFOUND)
  {
    Serial.println("Did not find a match");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  // found a match!
  Serial.print("Found ID #");
  Serial.print(finger.fingerID);
  Serial.print(" with confidence of ");
  Serial.println(finger.confidence);

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez()
{
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)
    return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)
    return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)
    return -1;

  // found a match!
  Serial.print("Found ID #");
  Serial.print(finger.fingerID);
  Serial.print(" with confidence of ");
  Serial.println(finger.confidence);
  return finger.fingerID;
}

Let us run through what each line of code does.

#include <Arduino.h>
#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

uint8_t getFingerprintID();

We import the header file for our Adafruit fingerprint sensor library and then define our HardwareSerial and Adafruit_Fingerprint classes.

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ; // For Yun/Leo/Micro/Zero/...
  delay(100);
  Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  delay(5);
  if (finger.verifyPassword())
  {
    Serial.println("Found fingerprint sensor!");
  }
  else
  {
    Serial.println("Did not find fingerprint sensor :(");
    while (1)
    {
      delay(1);
    }
  }

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x"));
  Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x"));
  Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: "));
  Serial.println(finger.capacity);
  Serial.print(F("Security level: "));
  Serial.println(finger.security_level);
  Serial.print(F("Device address: "));
  Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: "));
  Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: "));
  Serial.println(finger.baud_rate);

  finger.getTemplateCount();

  if (finger.templateCount == 0)
  {
    Serial.print("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
  }
  else
  {
    Serial.println("Waiting for valid finger...");
    Serial.print("Sensor contains ");
    Serial.print(finger.templateCount);
    Serial.println(" templates");
  }
}

In the setup() function, first, we initialize our Serial monitor and then communicate with our R307 database and print out the parameters. Next, we check for the number of fingerprints in our database by calling the finger.getTemplateCount();

void loop()
{
  getFingerprintID();
  delay(50); // don't ned to run this at full speed.
}

uint8_t getFingerprintID()
{
  uint8_t p = finger.getImage();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image taken");
    break;
  case FINGERPRINT_NOFINGER:
    Serial.println("No finger detected");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_IMAGEFAIL:
    Serial.println("Imaging error");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.println("Image converted");
    break;
  case FINGERPRINT_IMAGEMESS:
    Serial.println("Image too messy");
    return p;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  case FINGERPRINT_FEATUREFAIL:
    Serial.println("Could not find fingerprint features");
    return p;
  case FINGERPRINT_INVALIDIMAGE:
    Serial.println("Could not find fingerprint features");
    return p;
  default:
    Serial.println("Unknown error");
    return p;
  }

  // OK converted!
  p = finger.fingerSearch();
  if (p == FINGERPRINT_OK)
  {
    Serial.println("Found a print match!");
  }
  else if (p == FINGERPRINT_PACKETRECIEVEERR)
  {
    Serial.println("Communication error");
    return p;
  }
  else if (p == FINGERPRINT_NOTFOUND)
  {
    Serial.println("Did not find a match");
    return p;
  }
  else
  {
    Serial.println("Unknown error");
    return p;
  }

  // found a match!
  Serial.print("Found ID #");
  Serial.print(finger.fingerID);
  Serial.print(" with confidence of ");
  Serial.println(finger.confidence);

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez()
{
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)
    return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)
    return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)
    return -1;

  // found a match!
  Serial.print("Found ID #");
  Serial.print(finger.fingerID);
  Serial.print(" with confidence of ");
  Serial.println(finger.confidence);
  return finger.fingerID;
}

In the loop() function, we ask the user for their fingerprint twice and then use the function finger.fingerFastSearch(); to check if it is present in our database.

Download Fingerprint Template

If you want to view how our fingerprint template is stored on our database then you can use the following code below.

#include <Arduino.h>

#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

int getFingerprintIDez();
void printHex(int num, int precision);
uint8_t downloadFingerprintTemplate(uint16_t id);

void setup()
{
  while (!Serial)
    ;
  Serial.begin(9600);
  Serial.println("Fingerprint template extractor");

  // set the data rate for the sensor serial port
  finger.begin(57600);

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

  // Try to get the templates for fingers 1 through 10
  for (int finger = 1; finger < 10; finger++)
  {
    downloadFingerprintTemplate(finger);
  }
}

uint8_t downloadFingerprintTemplate(uint16_t id)
{
  Serial.println("------------------------------------");
  Serial.print("Attempting to load #");
  Serial.println(id);
  uint8_t p = finger.loadModel(id);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.print("Template ");
    Serial.print(id);
    Serial.println(" loaded");
    break;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  default:
    Serial.print("Unknown error ");
    Serial.println(p);
    return p;
  }

  // OK success!

  Serial.print("Attempting to get #");
  Serial.println(id);
  p = finger.getModel();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.print("Template ");
    Serial.print(id);
    Serial.println(" transferring:");
    break;
  default:
    Serial.print("Unknown error ");
    Serial.println(p);
    return p;
  }

  // one data packet is 267 bytes. in one data packet, 11 bytes are 'usesless' :D
  uint8_t bytesReceived[534]; // 2 data packets
  memset(bytesReceived, 0xff, 534);

  uint32_t starttime = millis();
  int i = 0;
  while (i < 534 && (millis() - starttime) < 20000)
  {
    if (serialPort.available())
    {
      bytesReceived[i++] = serialPort.read();
    }
  }
  Serial.print(i);
  Serial.println(" bytes read.");
  Serial.println("Decoding packet...");

  uint8_t fingerTemplate[512]; // the real template
  memset(fingerTemplate, 0xff, 512);

  // filtering only the data packets
  int uindx = 9, index = 0;
  memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // first 256 bytes
  uindx += 256;                                               // skip data
  uindx += 2;                                                 // skip checksum
  uindx += 9;                                                 // skip next header
  index += 256;                                               // advance pointer
  memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // second 256 bytes

  for (int i = 0; i < 512; ++i)
  {
    // Serial.print("0x");
    printHex(fingerTemplate[i], 2);
    // Serial.print(", ");
  }
  Serial.println("\ndone.");

  return p;

  /*
    uint8_t templateBuffer[256];
    memset(templateBuffer, 0xff, 256);  //zero out template buffer
    int index=0;
    uint32_t starttime = millis();
    while ((index < 256) && ((millis() - starttime) < 1000))
    {
    if (mySerial.available())
    {
      templateBuffer[index] = mySerial.read();
      index++;
    }
    }

    Serial.print(index); Serial.println(" bytes read");

    //dump entire templateBuffer.  This prints out 16 lines of 16 bytes
    for (int count= 0; count < 16; count++)
    {
    for (int i = 0; i < 16; i++)
    {
      Serial.print("0x");
      Serial.print(templateBuffer[count*16+i], HEX);
      Serial.print(", ");
    }
    Serial.println();
    }*/
}

void printHex(int num, int precision)
{
  char tmp[16];
  char format[128];

  sprintf(format, "%%.%dX", precision);

  sprintf(tmp, format, num);
  Serial.print(tmp);
}

void loop()
{
}

Let’s check what each line of code does.

#include <Arduino.h>

#include <Adafruit_Fingerprint.h>

HardwareSerial serialPort(2); // use UART2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&serialPort);

int getFingerprintIDez();
void printHex(int num, int precision);
uint8_t downloadFingerprintTemplate(uint16_t id);

Same as before, we import the Adafruit header file and declare the HardwareSerial and Adafruit_Fingerprint classes. After this, we forward-declared some of our functions so that we would fix the compilation errors.

void setup()
{
  while (!Serial)
    ;
  Serial.begin(9600);
  Serial.println("Fingerprint template extractor");

  // set the data rate for the sensor serial port
  finger.begin(57600);

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

  // Try to get the templates for fingers 1 through 10
  for (int finger = 1; finger < 10; finger++)
  {
    downloadFingerprintTemplate(finger);
  }
}

uint8_t downloadFingerprintTemplate(uint16_t id)
{
  Serial.println("------------------------------------");
  Serial.print("Attempting to load #");
  Serial.println(id);
  uint8_t p = finger.loadModel(id);
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.print("Template ");
    Serial.print(id);
    Serial.println(" loaded");
    break;
  case FINGERPRINT_PACKETRECIEVEERR:
    Serial.println("Communication error");
    return p;
  default:
    Serial.print("Unknown error ");
    Serial.println(p);
    return p;
  }

  // OK success!

  Serial.print("Attempting to get #");
  Serial.println(id);
  p = finger.getModel();
  switch (p)
  {
  case FINGERPRINT_OK:
    Serial.print("Template ");
    Serial.print(id);
    Serial.println(" transferring:");
    break;
  default:
    Serial.print("Unknown error ");
    Serial.println(p);
    return p;
  }

  // one data packet is 267 bytes. in one data packet, 11 bytes are 'usesless' :D
  uint8_t bytesReceived[534]; // 2 data packets
  memset(bytesReceived, 0xff, 534);

  uint32_t starttime = millis();
  int i = 0;
  while (i < 534 && (millis() - starttime) < 20000)
  {
    if (serialPort.available())
    {
      bytesReceived[i++] = serialPort.read();
    }
  }
  Serial.print(i);
  Serial.println(" bytes read.");
  Serial.println("Decoding packet...");

  uint8_t fingerTemplate[512]; // the real template
  memset(fingerTemplate, 0xff, 512);

  // filtering only the data packets
  int uindx = 9, index = 0;
  memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // first 256 bytes
  uindx += 256;                                               // skip data
  uindx += 2;                                                 // skip checksum
  uindx += 9;                                                 // skip next header
  index += 256;                                               // advance pointer
  memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // second 256 bytes

  for (int i = 0; i < 512; ++i)
  {
    // Serial.print("0x");
    printHex(fingerTemplate[i], 2);
    // Serial.print(", ");
  }
  Serial.println("\ndone.");

  return p;

  /*
    uint8_t templateBuffer[256];
    memset(templateBuffer, 0xff, 256);  //zero out template buffer
    int index=0;
    uint32_t starttime = millis();
    while ((index < 256) && ((millis() - starttime) < 1000))
    {
    if (mySerial.available())
    {
      templateBuffer[index] = mySerial.read();
      index++;
    }
    }

    Serial.print(index); Serial.println(" bytes read");

    //dump entire templateBuffer.  This prints out 16 lines of 16 bytes
    for (int count= 0; count < 16; count++)
    {
    for (int i = 0; i < 16; i++)
    {
      Serial.print("0x");
      Serial.print(templateBuffer[count*16+i], HEX);
      Serial.print(", ");
    }
    Serial.println();
    }*/
}

void printHex(int num, int precision)
{
  char tmp[16];
  char format[128];

  sprintf(format, "%%.%dX", precision);

  sprintf(tmp, format, num);
  Serial.print(tmp);
}

void loop()
{
}

First, we initialized our Serial monitor.

Next, start communicating with our R307 sensor.

Lastly, download the fingerprint templates by calling the downloadFingerprintTemplate(finger);

A sample output of the said program is shown below. Some of the attempt to download the fingerprint template causes some error so if you want to download the template then just call the downloadFingerprintTemplate(2); function with a specific function.

Fingerprint template extractor
Found fingerprint sensor!
------------------------------------
Attempting to load #1
Template 1 loaded
Attempting to get #1
Template 1 transferring:
534 bytes read.
Decoding packet...
030355180801320183000000000000000000000000000000000000000000000000000000000000000000000000000000090005007F00000C330030CF33FFFFFFFF03CFBABAAAAAAAAAAA6665656559555555044440400101010101010101010101010101010101010101010101010101010101010101010101010101010101011127EF01FFFFFFFF0200822F1563BE4FA0299E0AA684DE4827413E342A9ABE212B1B7E3899613F5FA569DF11AF5B9F4BB140BF0D33C49F5541977F3C93093C19191C9C629BE4FC61A0A87C2A2B43DC19C0D89C4294C5FD2615A03D2041037D1F985E5A591AE3BB6CC46ADF0000000000000000000000000000000000000000002946EF01FFFFFFFF02008200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084EF01FFFFFFFF0200820303581500012001820000000000000000000000000000000000000000000000000000000000000000000000000000000A0004007B0030030C003FC03FFFFFFFFF03FEABAAAAAAAAAAA65956565455555511440440000000000000000000000000000000000000000000
done.
------------------------------------
Attempting to load #2
Communication error
------------------------------------
Attempting to load #3
Communication error
------------------------------------
Attempting to load #4
Template 4 loaded
Attempting to get #4
Template 4 transferring:
534 bytes read.
Decoding packet...
00000AEF01FFFFFFFF07000300000AEF01FFFFFFFF02008203035A0D440170018A0000000000000000000000000000000000000000000000000000000000000000000000000000000D000A007D000C0CC3300CFF3FFFFFEEFFBBBBBAAEABAA6566A55A66555555515114440004010101010101010101010101010101010101010101010101010101010101010101010101010101010101011190EF01FFFFFFFF0200821AA69F3E3639D91E33BF405E40482BFE34CED87E3221A0FF4A2CA59F5C32A5FF59BD935F39ADD91C33AFEB1C3CA460F80E2F9D7E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000185AEF01FFFFFFFF0200820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078C40120FB4F00100400000078C40120049CEF01FFFFFFFF0200820303540C240149018A0000000000000000000000000000000000000000000000000000000000000000000000000000000300070075000000033030C033FFCFFEFFFBBBFBBBBAAAEAAA656A65555555555155
done.
------------------------------------
Attempting to load #5
Communication error
------------------------------------
Attempting to load #6
Communication error
------------------------------------
Attempting to load #7
Template 7 loaded
Attempting to get #7
Unknown error 12
------------------------------------
Attempting to load #8
Unknown error 12
------------------------------------
Attempting to load #9
Template 9 loaded
Attempting to get #9
Unknown error 1

That really is how you can interface your ESP32 microcontroller with your R307 fingerprint sensor using the Arduino code.

Common Question

Can I download the R307 fingerprint sensor image and saved it to the file or database?

When you are using the Adafruit fingerprint sensor library then the quick answer is NO as there is no API there that you could use. I tried looking into this also but the cannot find any information. Some say you can write your own library that can do this or you could use the USB option and save the scanned image into your database. You might need to roll on your own fingerprint comparison logic though to compare the images taken by this sensor.

Wrap Up

We have successfully set up our ESP32 Microcontroller to communicate with our R307 fingerprint sensor in this post. At the same time, we have run through the code on how to enroll, compare, and view our fingerprints using the Adafruit fingerprint sensor library. You can now begin adding this sensor to your next Internet of Things (IoT) project.

I hope you learned something! Happy exploring!

If you like my post then please consider sharing this. Thanks!

3 responses to “Interfacing ESP32 with R307 Fingerprint Sensor”

  1. Arduino Fingerprint Door Lock using ESP32 with a Web App

    […] Must Read: Interfacing ESP32 with R307 Fingerprint Sensor […]

  2. koushal Avatar
    koushal

    sir i am not getting enough confidence level in the fingerprint example does it mean that my fingerprint sensor have some issue? or what should i do correct it.

    1. donsky Avatar
      donsky

      Retry scanning your fingerprint again

Leave a Reply

Your email address will not be published. Required fields are marked *