@samlab-corp/sfm-node
v0.5.0
Published
SFM Protocol V3.6.0 based fingerprint recognition library for Node.js
Readme
@samlab-corp/sfm-node
Node.js library for Suprema SFM (Suprema Fingerprint Module) devices based on SFM Protocol Manual V3.6.0.
Supports fingerprint enrollment, verification (1:1), identification (1:N), template management, firmware upgrade, and continuous scanning via Serial (RS232), RS422/485, or TCP/IP.
Installation
npm install @samlab-corp/sfm-node
# or
yarn add @samlab-corp/sfm-nodeRequirements
- Node.js 18+
- A Suprema SFM fingerprint module connected via serial port or TCP/IP
Quick Start
import { SfmCommands } from "@samlab-corp/sfm-node";
// Create and connect in one step
const sfm = await SfmCommands.create({
portPath: "/dev/ttyUSB0",
baudRate: 115200,
});
// Enroll fingerprint (userId: 1)
await sfm.fingerprint.enroll(1);
// 1:1 Verification
await sfm.fingerprint.verify(1);
// 1:N Identification
await sfm.fingerprint.identify();
await sfm.disconnect();Constructor Options
const sfm = new SfmCommands({
portPath: "/dev/ttyUSB0", // Serial port path (used by create() / connect())
baudRate: 115200, // Serial baud rate (default: 115200)
debug: false, // Enable debug logging
commandTimeout: 20000, // Command response timeout in ms
readTimeout: 3000, // Data read timeout in ms
secureMode: false, // AES256 encryption mode
encryptionKey: Buffer, // AES256 encryption key (32 bytes)
iv: Buffer, // AES256 IV (16 bytes)
secureCode: Buffer, // Secure code
encryptionMode: "AES256_CBC", // Encryption mode ('AES256_CBC' | 'AES256_ECB')
networkMode: false, // RS422/485 network mode
terminalId: 0, // Network terminal ID
hexAsciiMode: false, // Hex-ASCII serial bridge mode
autoReconnect: {
// Auto-reconnect options
enabled: true,
interval: 3000,
maxRetries: 10,
},
});API Reference
Connection Management
| Method | Description |
| ------------------------------------------ | --------------------------------------------- |
| SfmCommands.create(options) | Create instance and connect in one step |
| SfmCommands.listPorts() | List available serial ports |
| sfm.connect(portPath?) | Connect to device (uses portPath from options if omitted) |
| sfm.disconnect() | Disconnect from device |
| sfm.setSecureMode(enabled, options?) | Toggle AES256 encryption mode |
| sfm.setNetworkMode(enabled, terminalId?) | Toggle RS422/485 network mode |
| sfm.setOnDisconnect(callback) | Register disconnect callback |
| sfm.setAutoReconnect(options) | Configure auto-reconnect |
| sfm.setAutoReconnectCallbacks(callbacks) | Register reconnect status callbacks |
| sfm.isReconnecting | Whether reconnection is in progress (boolean) |
sfm.fingerprint — Enrollment / Verification / Identification
| Method | Description | Return Type |
| ----------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------- |
| enroll(userId, flag?, options?) | Enroll fingerprint (auto-adapts to device ENROLL_MODE: 1-scan or 2-scan) | EnrollResult |
| enrollScan1(userId, flag?, options?) | First scan enrollment | EnrollResult |
| enrollScan2(options?) | Second scan enrollment (for SCAN2 modes) | EnrollResult |
| verify(userId, options?) | 1:1 Verification — match against a specific user | VerifyResult |
| identify(options?) | 1:N Identification — match against all enrolled fingerprints | IdentifyResult |
| deleteTemplate(userId) | Delete a specific user's fingerprint template | CommandResult |
| deleteAllTemplates() | Delete all fingerprint templates | CommandResult |
| listUserIds(options?) | List enrolled user IDs | ListUserIdsResult |
| readTemplate(userId, options?) | Read fingerprint template data from device (RTX protocol) | { success, templateData, error } |
| writeTemplate(userId, templateData, flag?, options?) | Write fingerprint template to device (ET protocol, 384B chunks) | { success, userId, error } |
| writeTemplateMulti(userId, templateData, flag?, options?) | Write fingerprint template to device (ETX extended protocol, multi-chunk) | { success, userId, error } |
| captureImage(options?) | Capture fingerprint image (SI command) | CaptureImageResult |
Enrollment Flags
Flags used with enroll, writeTemplate, etc.:
| Flag | Value | Description |
| ----------------------- | ------ | ------------------------------- |
| SFM_FLAG.NONE | 0x00 | No flag (default) |
| SFM_FLAG.CHECK_ID | 0x70 | Check for duplicate ID |
| SFM_FLAG.ADD_NEW | 0x71 | Add fingerprint to existing ID |
| SFM_FLAG.AUTO_ID | 0x79 | Auto-assign ID |
| SFM_FLAG.CHECK_FINGER | 0x84 | Check for duplicate fingerprint |
Examples
// Basic enrollment
const result = await sfm.fingerprint.enroll(1);
// Enroll with duplicate fingerprint check
const result = await sfm.fingerprint.enroll(1, SFM_FLAG.CHECK_FINGER);
// With log callback
const result = await sfm.fingerprint.verify(1, {
onLog: (msg) => console.log(msg),
});
// Template backup / restore
const { templateData } = await sfm.fingerprint.readTemplate(1);
await sfm.fingerprint.writeTemplate(2, templateData, SFM_FLAG.ADD_NEW);
// List enrolled users
const list = await sfm.fingerprint.listUserIds();
// { success: true, userIds: [1, 2, 3], count: 3, error: null }
// Capture fingerprint image
const image = await sfm.fingerprint.captureImage();
// { success: true, imageData: Buffer, width: 256, height: 288, error: null }Return Types
interface EnrollResult {
success: boolean;
step: number; // Current scan step (1 or 2)
userId?: number;
error?: string;
}
interface VerifyResult {
success: boolean;
userId?: number;
error: string | null;
}
interface IdentifyResult {
success: boolean;
userId: number;
error: string | null;
}
interface ListUserIdsResult {
success: boolean;
userIds: number[];
count: number; // Number of enrolled templates
error: string | null;
}
interface CaptureImageResult {
success: boolean;
imageData: Buffer | null;
width: number;
height: number;
error: string | null;
}sfm.system — System Configuration / Diagnostics
| Method | Description | Return Type |
| ------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------- |
| getSystemStatus() | Get module and sensor status | SystemStatusResult |
| getDeviceInfo() | Get firmware version, build number, serial number | { firmwareVersion, buildNumber, serialNumber, error } |
| getParam(key) | Read system parameter | ParamResult |
| setParam(key, value) | Write system parameter (RAM only) | CommandResult |
| writeConfig(key, value) | Write system parameter + save to flash (setParam + saveParam) | CommandResult |
| saveParam() | Save current parameters to flash | CommandResult |
| reset() | Reset module | CommandResult |
| selfTest(command?, dataSize?) | Communication self-test (data round-trip verification) | SelfTestResult |
Examples
// Device info
const info = await sfm.system.getDeviceInfo();
// { firmwareVersion: '3.10', buildNumber: '19.7.9.32', serialNumber: 12345, error: null }
// System status
const status = await sfm.system.getSystemStatus();
// { success: true, moduleStatusLabel: 'ALIVE', sensorStatusLabel: 'ALIVE', ... }
// Read parameter
const param = await sfm.system.getParam("SECURITY_LEVEL");
// { value: 0x33, error: null }
// Write parameter (string alias or numeric value)
await sfm.system.setParam("SECURITY_LEVEL", "HIGH");
await sfm.system.saveParam();
// Or use writeConfig to write + save in one call
await sfm.system.writeConfig("ENROLL_MODE", "SCAN2_MERGE");
// Self-test
const test = await sfm.system.selfTest();
// { success: true, sent: 65536, received: 65536, elapsed: 1200, error: null }Return Types
interface SystemStatusResult {
success: boolean;
moduleStatus?: number; // 0x30=ALIVE, 0x34=BUSY
moduleStatusLabel?: string;
sensorStatus?: number; // 0x30=ALIVE, 0x31=ERROR
sensorStatusLabel?: string;
error: string | null;
}
interface ParamResult {
value: number;
error: string | null;
}
interface CommandResult {
success: boolean;
error: string | null;
}
interface SelfTestResult {
success: boolean;
sent: number;
received: number;
elapsed: number; // ms
error: string | null;
}sfm.freeScan — Continuous Fingerprint Scanning Mode
The device automatically scans fingerprints and delivers identification results via callback.
| Method | Description | Return Type |
| -------------------------- | ---------------------------------------------------------- | ---------------------- |
| start(onResult, onLog?) | Start FreeScan (auto-configures AUTO_RESPONSE + FREE_SCAN) | { success, message } |
| stop() | Stop FreeScan | { success, message } |
| isRunning | Whether FreeScan is active (boolean) | — |
| isDeviceFreeScanActive() | Check if device FREE_SCAN parameter is ON | boolean |
| onDataHandler(handler) | Register external data handler | — |
| offDataHandler() | Remove external data handler | — |
| onErrorHandler(handler) | Register external error handler | — |
| offErrorHandler() | Remove external error handler | — |
Examples
// Start FreeScan
await sfm.freeScan.start(
(result) => {
if (result.success) {
console.log(`Identified user: ${result.userId}`);
} else {
console.log("Unregistered fingerprint");
}
},
(msg) => console.log(msg), // Log callback (optional)
);
// Check status
console.log(sfm.freeScan.isRunning); // true
// Stop FreeScan
await sfm.freeScan.stop();Callback Types
interface FreeScanResult {
success: boolean;
userId?: number; // User ID on successful identification
flag?: number; // Error flag on failure
}
type FreeScanCallback = (result: FreeScanResult) => void;sfm.firmware — Firmware Upgrade
| Method | Description |
| ---------------------------------------------------- | ---------------------------------------- |
| upgrade(filePath, options?) | Firmware upgrade via serial handshake |
| upgradeViaProtocol(filePath, chunkSize?, options?) | Firmware upgrade via SFM packet protocol |
| loadFirmware(filePath) | Load firmware file and build packet |
Examples
// Serial handshake method (typical)
await sfm.firmware.upgrade("/path/to/firmware.bin", {
onProgress: (percent, sent, total) => console.log(`${percent}%`),
onStatus: (msg) => console.log(msg),
});
// Protocol method
await sfm.firmware.upgradeViaProtocol("/path/to/firmware.bin");Options Type
interface FirmwareOptions {
onProgress?: (percent: number, sent: number, total: number) => void;
onStatus?: (message: string) => void;
}sfm.data — Multi-Packet Data Transfer
Low-level multi-packet data transfer. Handles checksum verification and AES256 encryption automatically.
| Method | Description |
| ------------------------------------------- | ------------------------- |
| send(command, data, chunkSize?, options?) | Send multi-packet data |
| recv(command, totalSize, options?) | Receive multi-packet data |
await sfm.data.send(command, buffer, 0x4000, {
onProgress: (packetIndex, totalPackets) => { ... },
});
const data = await sfm.data.recv(command, totalSize);System Parameter Key Reference
Keys and available values for sfm.system.getParam(key), sfm.system.setParam(key, value), and sfm.system.writeConfig(key, value).
Timeout / Timing
| Key | Description | Values |
| ------------------ | -------------------------------------- | ------------------------------------------------------------------------------- |
| TIMEOUT | Fingerprint input wait time | 'INFINITE', '1S', '2S', '3S', '4S', '5S', '10S', '15S', '20S' |
| MATCHING_TIMEOUT | Matching timeout | 'INFINITE', '1S', '3S', '5S', '10S', '20S' |
| RESPONSE_DELAY | Response delay | 'NONE', '20MS', '40MS', '60MS', '80MS', '100MS', '200MS' |
| FREE_SCAN_DELAY | FreeScan consecutive recognition delay | 'NONE', '1S', '2S', '3S', '5S', '10S' |
Enrollment / Authentication
| Key | Description | Values |
| --------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| ENROLL_MODE | Enrollment mode | 'SCAN1' (single scan), 'SCAN2_MERGE' (2 scans merged), 'SCAN2_DUAL' (2 scans stored separately) |
| SECURITY_LEVEL | Security level (FAR) | 'LOW', 'NORMAL', 'HIGH', 'HIGHEST', 'AUTO_NORMAL', 'AUTO_SECURE', 'AUTO_MORE_SECURE' |
| ENROLL_DISPLACEMENT | Finger displacement during enrollment | 'NONE', '1MM', '3MM', '5MM', '10MM' |
| PROVISIONAL_ENROLL | Provisional enrollment mode | 'PERMANENT', 'TEMPORARY' |
| PASS_WHEN_EMPTY | Authentication result when DB is empty | 'FAIL', 'PASS' |
Sensor / Image
| Key | Description | Values |
| -------------------- | --------------------------- | ---------------------------------------------------------------- |
| SENSOR_TYPE | Sensor type (read-only) | 'FC', 'OPTICAL', 'TC', 'OC', 'OL', 'FOH02', 'TS4' |
| SENSITIVITY | Sensor sensitivity | 'LV0' ~ 'LV7' |
| IMAGE_FORMAT | Image transfer format | 'RAW_GRAY', 'BINARY', 'GRAY_4BIT', 'WSQ' |
| IMAGE_QUALITY | Image quality threshold | 'WEAK', 'MODERATE', 'STRONG', 'STRONGEST' |
| ROTATE_IMAGE | Sensor image rotation | 'NORMAL', 'FLIP' |
| ROTATION | Matching rotation tolerance | 'DEG15', 'DEG30', 'DEG45', 'DEG60', 'DEG75', 'DEG90' |
| LIGHTING_CONDITION | Lighting environment | 'OUTDOOR', 'INDOOR' |
Communication
| Key | Description | Values |
| ------------------- | -------------------------- | ----------------------------------------------------------------------------------------- |
| BAUDRATE | Baud rate | '9600', '19200', '38400', '57600', '115200', '230400', '460800', '921600' |
| ENCRYPTION_MODE | Encryption mode | 'OFF', 'ON' |
| ASCII_PACKET | Packet exchange format | 'HEX', 'ASCII' |
| NETWORK_MODE | Network communication mode | 'SINGLE', 'NETWORK' |
| SEND_SCAN_SUCCESS | Send SCAN_SUCCESS response | 'OFF', 'ON' |
Operation Mode
| Key | Description | Values |
| --------------- | --------------------------- | ------------------------------------------------- |
| FREE_SCAN | Idle-state auto scan | 'OFF', 'ON' |
| AUTO_RESPONSE | GPIO/FreeScan auto response | 'OFF', 'HOST', 'AUX', 'BOTH' |
| FAST_MODE | 1:N matching speed boost | 'OFF', 'LV1' ~ 'LV4', 'FASTEST', 'AUTO' |
| WATCHDOG | Watchdog timer | 'OFF', 'ON' |
Template / Security
| Key | Description | Values |
| -------------------------- | ---------------------------- | ----------------------------------------- |
| TEMPLATE_TYPE | Template type | 'SUPREMA', 'ISO', 'ANSI' |
| ENHANCED_PRIVACY | Enhanced privacy mode | 'OFF', 'ON' |
| FAKE_FINGER_DETECTION | Fake finger detection | 'OFF', 'WEAK', 'NORMAL', 'STRONG' |
| CHECK_LATENT_FINGERPRINT | Latent fingerprint detection | 'ENROLL_ONLY', 'NEVER', 'ALWAYS' |
Read-Only Parameters
| Key | Description |
| ------------------ | ------------------------------------- |
| TEMPLATE_SIZE | Template size |
| MODULE_ID | Module ID |
| FIRMWARE_VERSION | Firmware version |
| SERIAL_NUMBER | Serial number |
| BUILD_NUMBER | Build number |
| ENROLLED_FINGER | Number of enrolled fingerprints |
| AVAILABLE_FINGER | Number of available fingerprint slots |
| BAUDRATE2 | Secondary baud rate |
| VOLTAGE_WARNING | Voltage warning threshold |
| POWER_OFF | Auto power-off timeout |
Error Codes
| Code | Value | Description |
| ------------------------ | ------ | ------------------------------------ |
| SFM_ERROR.SUCCESS | 0x61 | Success |
| SFM_ERROR.SCAN_SUCCESS | 0x62 | Scan success (intermediate response) |
| SFM_ERROR.SCAN_FAIL | 0x63 | Scan failed |
| SFM_ERROR.NOT_FOUND | 0x69 | Data not found |
| SFM_ERROR.NOT_MATCH | 0x6a | Mismatch |
| SFM_ERROR.TRY_AGAIN | 0x6b | Retry needed (poor image quality) |
| SFM_ERROR.TIME_OUT | 0x6c | Timeout |
| SFM_ERROR.MEM_FULL | 0x6d | Memory full |
| SFM_ERROR.EXIST_ID | 0x6e | ID already exists |
| SFM_ERROR.CONTINUE | 0x74 | Continue data transfer |
| SFM_ERROR.UNSUPPORTED | 0x75 | Unsupported command |
TypeScript
All types are exported:
import type {
SfmClientOptions,
EnrollResult,
VerifyResult,
IdentifyResult,
CommandResult,
ListUserIdsResult,
CaptureImageResult,
SystemStatusResult,
ParamResult,
SelfTestResult,
FreeScanResult,
FreeScanCallback,
FirmwareOptions,
} from "@samlab-corp/sfm-node";