Interfacing to Adafruit fingerprint sensor

Posted on
  • I'm just copying a some code here from a PM. @ChrisB was asking about the Adafruit fingerprint sensor...

    Connections:

    Reader Espruino
    Vin / power input (Red) Vbat
    TD / Data output TTL Logical (green) C11
    RD / Data input TTL logical (white) C10
    GND (black) GND

    The module itself needs writing to that it can cope with the 'timeout' used by Adafruit's module while still allowing tasks to run in the background - so it needs to use callbacks...

    So far, the initial suggested module looks like:

    receivedData=[];
    Serial4.setup(57600);
    Serial4.onData(function (e) { receivedData.push(e.data.charCodeAt(0));­ });
    
    var C = {
    OK: 0x00,
    PACKETRECIEVEERR: 0x01,
    NOFINGER: 0x02,
    IMAGEFAIL: 0x03,
    IMAGEMESS: 0x06,
    FEATUREFAIL: 0x07,
    NOMATCH: 0x08,
    NOTFOUND: 0x09,
    ENROLLMISMATCH: 0x0A,
    BADLOCATION: 0x0B,
    DBRANGEFAIL: 0x0C,
    UPLOADFEATUREFAIL: 0x0D,
    PACKETRESPONSEFAIL: 0x0E,
    UPLOADFAIL: 0x0F,
    DELETEFAIL: 0x10,
    DBCLEARFAIL: 0x11,
    PASSFAIL: 0x13,
    INVALIDIMAGE: 0x15,
    FLASHERR: 0x18,
    INVALIDREG: 0x1A,
    ADDRCODE: 0x20,
    PASSVERIFY: 0x21,
    STARTCODE: 0xEF01,
    COMMANDPACKET: 0x1,
    DATAPACKET: 0x2,
    ACKPACKET: 0x7,
    ENDDATAPACKET: 0x8,
    TIMEOUT: 0xFF,
    BADPACKET: 0xFE,
    GETIMAGE: 0x01,
    IMAGE2TZ: 0x02,
    REGMODEL: 0x05,
    STORE: 0x06,
    EMPTY: 0x0D,
    VERIFYPASSWORD: 0x13,
    HISPEEDSEARCH: 0x1B,
    TEMPLATECOUNT: 0x1D
    };
    
    var thePassword = 0;
    var theAddress = 0xFFFFFFFF;
    
    function writePacket(addr, packetType, packet) {
      var len = packet.length+2;
      Serial4.write([
            (C.STARTCODE >> 8) & 0xFF), 
            C.STARTCODE & 0xFF,
            (addr >> 24) & 0xFF,
            (addr >> 16) & 0xFF,
            (addr >> 8) & 0xFF,
            addr & 0xFF,
            packetType,
            (len >> 8) & 0xFF,
            len &0xFF]);
      
      var sum = (len>>8) + (len&0xFF) + packetType;
      Serial4.write(packet);
      for (var i in packet) sum += packet[i];
      Serial4.write([(sum>>8)&0xFF, sum&0xFF])
    }
    
    function getReply(callback) {
      receivedData=[];
      setTimeout(function() {
       // do some checks here?
       callback(receivedData);
      }, 500); // 500ms
    }
    
    
    function verifyPassword(callback) {
      var packet = [
        C.VERIFYPASSWORD, 
        (thePassword >> 24) & 0xFF, 
        (thePassword >> 16) & 0xFF,
        (thePassword >> 8) & 0xFF, 
        thePassword & 0xFF
      ];
      writePacket(theAddress, C.COMMANDPACKET, packet);
      getReply(function(packet) {
       callback((packet[0] == C.ACKPACKET) && (packet[1] == C.OK));
      });    
    }
    
  • Hi Gordon,
    I've modified your code and implemented some additional methods.
    I should have everything for reading, but cannot test as I need to add the Enroll (new fingerprint) part as there is no fingerID in the sensor yet :)

    I might have something within next few days...
    And then ready for a module...

  • But verifyPassword seems to work as expected? That appears to be a nice way to check that everything is working...

  • I've used your veryfiyPassword and writePacket.
    I've added the getReply logic and added getImage, image2Tz, fingerFastSearch
    I can receive bytes, and for example the getImage seems to react correctly when a finger is present or not.
    I cannot do more testing as the sensor is empy of registered fingers.
    I need to add the Enroll methods

  • And here is the code I have so far.
    Please note it was late, not necessarly optimized and the way the onData is done might change later :)
    Also I will add an 'err' as first parameter for each callback in case,
    and adapt some part of code.
    Once Enroll is done, I will start transforming and prepare for module

    var C = {
      FINGERPRINT_OK                 : 0x00,
      FINGERPRINT_PACKETRECIEVEERR   : 0x01,
      FINGERPRINT_NOFINGER           : 0x02,
      FINGERPRINT_IMAGEFAIL          : 0x03,
      FINGERPRINT_IMAGEMESS          : 0x06,
      FINGERPRINT_FEATUREFAIL        : 0x07,
      FINGERPRINT_NOMATCH            : 0x08,
      FINGERPRINT_NOTFOUND           : 0x09,
      FINGERPRINT_ENROLLMISMATCH     : 0x0A,
      FINGERPRINT_BADLOCATION        : 0x0B,
      FINGERPRINT_DBRANGEFAIL        : 0x0C,
      FINGERPRINT_UPLOADFEATUREFAIL  : 0x0D,
      FINGERPRINT_PACKETRESPONSEFAIL : 0x0E,
      FINGERPRINT_UPLOADFAIL         : 0x0F,
      FINGERPRINT_DELETEFAIL         : 0x10,
      FINGERPRINT_DBCLEARFAIL        : 0x11,
      FINGERPRINT_PASSFAIL           : 0x13,
      FINGERPRINT_INVALIDIMAGE       : 0x15,
      FINGERPRINT_FLASHERR           : 0x18,
      FINGERPRINT_INVALIDREG         : 0x1A,
      FINGERPRINT_ADDRCODE           : 0x20,
      FINGERPRINT_PASSVERIFY         : 0x21,
    
      FINGERPRINT_STARTCODE          : 0xEF01,
    
      FINGERPRINT_COMMANDPACKET      : 0x1,
      FINGERPRINT_DATAPACKET         : 0x2,
      FINGERPRINT_ACKPACKET          : 0x7,
      FINGERPRINT_ENDDATAPACKET      : 0x8,
    
      FINGERPRINT_TIMEOUT            : 0xFF,
      FINGERPRINT_BADPACKET          : 0xFE,
    
      FINGERPRINT_GETIMAGE           : 0x01,
      FINGERPRINT_IMAGE2TZ           : 0x02,
      FINGERPRINT_REGMODEL           : 0x05,
      FINGERPRINT_STORE              : 0x06,
      FINGERPRINT_EMPTY              : 0x0D,
      FINGERPRINT_VERIFYPASSWORD     : 0x13,
      FINGERPRINT_HISPEEDSEARCH      : 0x1B,
      FINGERPRINT_TEMPLATECOUNT      : 0x1D,
    
      DEFAULTTIMEOUT : 5000 // milliseconds
    };
    
    Serial4.setup(57600);
    
    var thePassword = 0;
    var theAddress = 0xFFFFFFFF;
    
    function writePacket(addr, packetType, packet) {
      var len = packet.length+2;
      Serial4.write([
        (C.FINGERPRINT_STARTCODE >> 8) & 0xFF,
        C.FINGERPRINT_STARTCODE & 0xFF,
        (addr >> 24) & 0xFF,
        (addr >> 16) & 0xFF,
        (addr >> 8) & 0xFF,
        addr & 0xFF,
        packetType,
        (len >> 8) & 0xFF,
        len &0xFF
      ]);
    
      var sum = (len>>8) + (len&0xFF) + packetType;
      Serial4.write(packet);
      for (var i in packet) sum += packet[i];
      Serial4.write([(sum>>8)&0xFF, sum&0xFF]);
    }
    
    function getReply(packet, callback) {
    
      var receivedData = [];// new Uint8Array(20);
    
      Serial4.onData(function (e) {
        receivedData.push(e.data.charCodeAt(0));­
    
        if ( (idx === 0) && (receivedData[0] != ( (C.FINGERPRINT_STARTCODE >> 8) & 0xFF) ))
          return;
    
        if (receivedData.length >= 9) {
    
          if (( receivedData[0] != (C.FINGERPRINT_STARTCODE >> 8)) ||
              ( receivedData[1] != (C.FINGERPRINT_STARTCODE & 0xFF)))
            console.log('bad packet');
    
          var packettype = receivedData[6];
          var len = receivedData[7];
    
          len = len << 8 & 0xFF;
          len = len | (receivedData[8] & 0xFF);
          len -= 2;
    
          if (receivedData.length <= (len+10))
            return;
    
          packet[0] = packettype;
    
          for (var i=0; i<len; i++) {
            packet[1+i] = receivedData[9+i];
          }
    
          console.log('end');  
          callback(len, packet);
        }
      });
    }
    
    function verifyPassword(callback) {
      var packet = [
        C.FINGERPRINT_VERIFYPASSWORD,
        (thePassword >> 24) & 0xFF,
        (thePassword >> 16) & 0xFF,
        (thePassword >> 8) & 0xFF,
        thePassword & 0xFF
      ];
    
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
    
      getReply(packet, function(len, packet) {
        if ((len == 1) && (packet[0] == C.FINGERPRINT_ACKPACKET) && (packet[1] == C.FINGERPRINT_OK))
          callback(true);
        else
          callback(false);
      });
    }
    
    function getImage(callback) {
      var packet = [C.FINGERPRINT_GETIMAGE];
    
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
    
      getReply(packet, function(len, packet) {
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET))
          callback(-1);
        else
          callback(packet[1]);
      });
    }
    
    function image2Tz(slot, callback) {
      var packet = [C.FINGERPRINT_IMAGE2TZ, (slot & 0xFF)];
    
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
    
      getReply(packet, function(len, packet) {
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET))
          callback(-1);
        else
          callback(packet[1]);
      });
    }
    
    function fingerFastSearch(callback) {
      var fingerID = 0xFFFF;
      var confidence = 0xFFFF;
      var packet = [C.FINGERPRINT_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x00, 0xA3];
    
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
    
      getReply(packet, function(len, packet) {
    
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET)) {
          callback(-1);
          return;
        }
    
        fingerID = packet[2];
        fingerID <<= 8;
        fingerID |= packet[3];
    
        confidence = packet[4];
        confidence <<= 8;
        confidence |= packet[5];
    
        callback(packet[1], fingerID, confidence);
      });
    }
    
    function getFingerprintIDez(callback) {
      getImage(function(f) {
        if (f != C.FINGERPRINT_OK) {
          callback(-1);
          return;
        }
        image2Tz(null, function(g) {
          if (g != C.FINGERPRINT_OK) {
            callback(-1);
            return;
          }
          fingerFastSearch(function(res, fingerID, confidence) {
            if (res != C.FINGERPRINT_OK) {
              callback(-1);
              return;
            }
    
            callback(fingerID);
    
          });
        });
      });
    }
    
    
    //setup
    verifyPassword(function(e) {
      if(e) {
        getFingerprintIDez(function(e) {
          console.log(e);
        });
      }
    });
    
  • I thought about creating a module just for interfacing with the sensor,
    and then another library, module, or code example that automate
    enrollment, and add if possible schedule management like authorize some fingerID only on certain day or so

  • Great! It all sounds good - let's hope it works once the fingerprint is enrolled :)

    If enrollment isn't that complicated it may be it's best just to make it a code example rather than another module... Up to you really...

  • I will see....
    The logic for enrollment will definitely use the interface module.
    This module have to contains all the logic to interface the sensor.

    But the logic to enroll a fingerID is quite long, as it require for example to take three image, compare, etc etc.
    And I thought that all those code might make the base module heavier in term of size. That's why I thought about additional module.. will see...
    But code example at minimum :)

  • Hi,
    I finally managed to make the fingerprint sensor working,
    with print recognition and code for enrollment.
    Here is the code :

    var C = {
      FINGERPRINT_OK                 : 0x00,
      FINGERPRINT_PACKETRECIEVEERR   : 0x01,
      FINGERPRINT_NOFINGER           : 0x02,
      FINGERPRINT_IMAGEFAIL          : 0x03,
      FINGERPRINT_IMAGEMESS          : 0x06,
      FINGERPRINT_FEATUREFAIL        : 0x07,
      FINGERPRINT_NOMATCH            : 0x08,
      FINGERPRINT_NOTFOUND           : 0x09,
      FINGERPRINT_ENROLLMISMATCH     : 0x0A,
      FINGERPRINT_BADLOCATION        : 0x0B,
      FINGERPRINT_DBRANGEFAIL        : 0x0C,
      FINGERPRINT_UPLOADFEATUREFAIL  : 0x0D,
      FINGERPRINT_PACKETRESPONSEFAIL : 0x0E,
      FINGERPRINT_UPLOADFAIL         : 0x0F,
      FINGERPRINT_DELETEFAIL         : 0x10,
      FINGERPRINT_DBCLEARFAIL        : 0x11,
      FINGERPRINT_PASSFAIL           : 0x13,
      FINGERPRINT_INVALIDIMAGE       : 0x15,
      FINGERPRINT_FLASHERR           : 0x18,
      FINGERPRINT_INVALIDREG         : 0x1A,
      FINGERPRINT_ADDRCODE           : 0x20,
      FINGERPRINT_PASSVERIFY         : 0x21,
      //
      FINGERPRINT_STARTCODE          : 0xEF01,
      //
      FINGERPRINT_COMMANDPACKET      : 0x1,
      FINGERPRINT_DATAPACKET         : 0x2,
      FINGERPRINT_ACKPACKET          : 0x7,
      FINGERPRINT_ENDDATAPACKET      : 0x8,
      //
      FINGERPRINT_TIMEOUT            : 0xFF,
      FINGERPRINT_BADPACKET          : 0xFE,
      //
      FINGERPRINT_GETIMAGE           : 0x01,
      FINGERPRINT_IMAGE2TZ           : 0x02,
      FINGERPRINT_REGMODEL           : 0x05,
      FINGERPRINT_STORE              : 0x06,
      FINGERPRINT_EMPTY              : 0x0D,
      FINGERPRINT_VERIFYPASSWORD     : 0x13,
      FINGERPRINT_HISPEEDSEARCH      : 0x1B,
      FINGERPRINT_TEMPLATECOUNT      : 0x1D,
      //
      DEFAULTTIMEOUT : 5000 // milliseconds
    };
    
    var serialTTL = null;
    var thePassword = 0;
    var theAddress = 0xFFFFFFFF;
    
    function connect(serial) {
      serialTTL = serial;
      serialTTL.setup(57600);
    }
    
    function checkSensor(callback) {
      var packet = [
        C.FINGERPRINT_VERIFYPASSWORD,
        (thePassword >> 24) & 0xFF,
        (thePassword >> 16) & 0xFF,
        (thePassword >> 8) & 0xFF,
        thePassword & 0xFF
      ];
      sendPacket(packet, callback);
    }
    
    function getImage(callback) {
      sendPacket([C.FINGERPRINT_GETIMAGE], callback);
    }
    
    function image2Tz(slot, callback) {
      sendPacket([C.FINGERPRINT_IMAGE2TZ, (slot & 0xFF)], callback);
    }
    
    function createModel(callback) {
      sendPacket([C.FINGERPRINT_REGMODEL], callback);
    }
    
    function storeModel(id, callback) {
      sendPacket([C.FINGERPRINT_STORE, 0x01, (id >> 8) & 0xFF, id & 0xFF], callback);
    }
    
    function emptyDatabase(callback) {
      sendPacket([C.FINGERPRINT_EMPTY], callback);
    }
    
    function fingerFastSearch(callback) {
      var fingerID = 0xFFFF;
      var confidence = 0xFFFF;
      var packet = [C.FINGERPRINT_HISPEEDSEARCH, 0x01 & 0xFF, 0x00  & 0xFF, 0x00 & 0xFF, 0x00 & 0xFF, 0xA3 & 0xFF];
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
      getReply(packet, function(len, packet) {
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET)) {
          callback(-1, null);
          return;
        }
        fingerID = packet[2];
        fingerID <<= 8;
        fingerID |= packet[3];
        confidence = packet[4];
        confidence <<= 8;
        confidence |= packet[5];
        callback(null, {'responseCode': packet[1], 'fingerID': fingerID, 'confidence': confidence});
      });
    }
    
    function getTemplateCount(callback) {
      var templateCount = 0xFFFF;
      var packet = [C.FINGERPRINT_TEMPLATECOUNT];
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
      getReply(packet, function(len, packet) {
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET)) {
          callback(-1, null);
          return;
        }
        templateCount = packet[2];
        templateCount <<= 8;
        templateCount |= packet[3];
        callback(null, {'responseCode': packet[1], 'templateCount': templateCount});
      });
    }
    
    function writePacket(addr, packetType, packet) {
      var len = packet.length+2;
      serialTTL.write([
        (C.FINGERPRINT_STARTCODE >> 8) & 0xFF,
        C.FINGERPRINT_STARTCODE & 0xFF,
        (addr >> 24) & 0xFF,
        (addr >> 16) & 0xFF,
        (addr >> 8) & 0xFF,
        addr & 0xFF,
        packetType,
        (len >> 8) & 0xFF,
        len &0xFF
      ]);
      var sum = (len>>8) + (len&0xFF) + packetType;
      serialTTL.write(packet);
      for (var i in packet) sum += packet[i];
      serialTTL.write([(sum>>8)&0xFF, sum&0xFF]);
    }
    
    function getReply(packet, callback) {
      var receivedData = [];
    
      serialTTL.onData(function (e) {
        receivedData.push(e.data.charCodeAt(0));­
    
        if ( (idx === 0) && (receivedData[0] != ( (C.FINGERPRINT_STARTCODE >> 8) & 0xFF) ))
          return;
    
        if (receivedData.length >= 9) {
          if (( receivedData[0] != (C.FINGERPRINT_STARTCODE >> 8)) ||
              ( receivedData[1] != (C.FINGERPRINT_STARTCODE & 0xFF))) {
            callback(-1, {'responseCode': C.FINGERPRINT_BADPACKET});
            return;
          }
    
          var packettype = receivedData[6];
          var len = receivedData[7];
    
          len <<= 8;
          len |= receivedData[8];
          len -= 2;
    
          if (receivedData.length <= (len+10))
            return;
    
          packet[0] = packettype;
    
          for (var i=0; i<len; i++) {
            packet[1+i] = receivedData[9+i];
          }
    
          callback(len, packet);
        }
      });
    }
    
    function sendPacket(packet, callback) {
      writePacket(theAddress, C.FINGERPRINT_COMMANDPACKET, packet);
      getReply(packet, function(len, packet) {
        if ((len != 1) && (packet[0] != C.FINGERPRINT_ACKPACKET))
          callback(-1, null);
        else
          callback(len, {'responseCode': packet[1]});
      });
    }
    
    function getErrorCode(responseCode) {
      var res = null;
      switch (responseCode & 0xFF) {
        case C.FINGERPRINT_OK:
          res = null;
          break;
        case C.FINGERPRINT_NOFINGER:
          res = "No Finger";
          break;
        case C.FINGERPRINT_PACKETRECIEVEERR:
          res = "Communication error";
          break;
        case C.FINGERPRINT_IMAGEFAIL:
          res = "Imaging error";
          break;
        case C.FINGERPRINT_IMAGEMESS:
          res = "Image too messy";
          break;
        case C.FINGERPRINT_FEATUREFAIL:
          res = "Could not find fingerprint features";
          break;
        case C.FINGERPRINT_INVALIDIMAGE:
          res = "Could not find fingerprint features";
          break;
        case C.FINGERPRINT_ENROLLMISMATCH:
          res = "Prints did not match";
          break;
        case C.FINGERPRINT_BADLOCATION:
          res = "Could not store in that location";
          break;
        case C.FINGERPRINT_BADPACKET:
          res = "Communication error";
          break;
        case C.FINGERPRINT_FLASHERR:
          res = "Error writing to flash";
          break;
        default:
          res = "Unknown error";
      }
      return res;
    }
    
  • And here is some example using the library :
    Async function series (Thanks Gordon) :

    function series(arr,done) {
      var i=0;
      function next(err, result) {
        if (((err!==undefined) && (err!==null)) || i>=arr.length)
          done(err, result);
        else
          setTimeout(function() {arr[i++](next);}, 0);
      }
      next();
    }
    

    Enrollment example :

    function enrollFingerprint(id) {
      series([
        function(done) {
          getImage(function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Image taken");
            done(error);
          });
        },
        function(done) {
          image2Tz(1, function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Image converted");
            done(error);
          });
        },
        function(done) {
          console.log('Remove finger');
          setTimeout(function(e) { console.log(e.time); done(null); }, 2000);
        },
        function(done) {
          console.log('Place same finger again');
          setTimeout(function(e) { console.log(e.time); done(null);}, 2000);
        },
        function(done) {
          getImage(function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Image taken");
            done(error);
          });
        },
        function(done) {
          image2Tz(2, function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Image converted");
            done(error);
          });
        },
        function(done) {
          createModel(function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Prints matched");
            done(error);
          });
        },
        function(done) {
          storeModel(id, function(err, result) {
            var error = getErrorCode(result.responseCode);
            if(error === null && ((result.responseCode & 0xFF) == C.FINGERPRINT_OK)) 
                console.log("Stored");
            done(error);
          });
        }
      ], function (err, result) {
        console.log(err);
      });
    }
    
    connect(Serial4);
    
    enrollFingerprint(8);
    

    Recogniton example :

    function getFingerprintIDez(callback) {
      series([
          function(done) {
            getImage(function(err, result) {
              var error = getErrorCode(result.responseCode);
              done(error);
            });
          },
          function(done) {
            image2Tz(1, function(err, result) {
              var error = getErrorCode(result.responseCode);
              done(error);
            });
          },
          function(done) {
            fingerFastSearch(function(err, result) {
              var error = getErrorCode(result.responseCode);
              done(error, result);
            });
          }
      ], function(err, result) {
           callback(err, result);
        }
      );
    }
    
    connect(Serial4);
    
    getFingerprintIDez(function(err, result) {
      console.log('final');
      console.log('Error : ' + err);
      if(err === null) {
      console.log(result);
        console.log('#ID : ' + result.fingerID);
        console.log('level : ' + result.confidence); 
      }
    });
    
  • That's brilliant - thanks!

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Interfacing to Adafruit fingerprint sensor

Posted by Avatar for Gordon @Gordon

Actions