UNPKG

mifare-reader-js

Version:

A TypeScript library for interfacing with MIFARE card readers via serial port communication

541 lines (381 loc) 13 kB
# mifare-reader-js A TypeScript library for interfacing with MIFARE card readers via serial port communication. This library provides a clean, promise-based API for communicating with MIFARE Classic RFID cards through serial port connections, commonly used with devices like the CR522AU or similar card readers. ## Features - **Multi-Format Support**: Works with both CommonJS and ES Modules - **Full TypeScript Support**: Complete type definitions for all APIs - **Serial Communication**: Robust serial port communication using `serialport` library - **Retry Logic**: Built-in initialization retry mechanism (up to 20 attempts by default) - **Timeout Handling**: Configurable command timeouts prevent hanging operations - **Hardware Integration**: - LED control (4 color states: GREEN, RED, YELLOW, OFF) - Audio feedback (beep functionality) - Card detection and reading - RF authentication with key modes (A/B) ## Tested Devices This library has been tested and confirmed to work with the following MIFARE card reader devices: | Brand | Model | Made By | Baudrate | Notes | |-------|-------|---------|----------|-------| | NfcPass | CR522AU-V7| ChinaReader | 19200 | N/A | > **Contributing**: If you've successfully tested this library with a specific reader model, please consider contributing by adding the device information to this table. Include the brand name, model number, manufacturer, and working baudrate. ## Installation ### Prerequisites - Node.js >= 14.0.0 - A MIFARE card reader connected via serial port - **Compatible driver for the reader device installed** - **Windows**: After plugging in the device, it should be recognized in Device Manager under "Ports (COM & LPT)" section (e.g., COM3, COM5) - **Linux**: The device should appear as `/dev/ttyUSB0`, `/dev/ttyACM0`, or similar. Check with `ls -la /dev/tty*` - **macOS**: The device should appear as `/dev/cu.usbserial-*` or similar. Check with `ls -la /dev/tty.* /dev/cu.*` - If the device is not recognized, install the appropriate USB-to-Serial driver (e.g., CH340, CP2102, FTDI drivers) ### Install from npm ```bash npm install mifare-reader-js ``` ### Install from Local Directory ```bash npm install /path/to/mifare-reader-js ``` ### Install from Package File ```bash npm pack npm install ./mifare-reader-js-1.0.0.tgz ``` ## Quick Start > **Note**: If the device does not respond during initialization, try changing the baudrate. Common values are `9600`, `19200`, `57600`, or `115200`. Different reader models may use different default baudrates. ### TypeScript Example ```typescript import { MifareReader, COLORS } from 'mifare-reader-js'; const reader = new MifareReader(); async function readCard() { try { // Initialize reader on COM3 at 19200 baud await reader.initialize('COM3', 19200); // Detect a card const cardId = await reader.selectCard(); if (cardId) { console.log('Card detected:', cardId); // Visual feedback: turn LED green await reader.changeLedColor(COLORS.GREEN); // Read card data with default MIFARE key const data = await reader.readCard(); console.log('Card data:', data); // Audio feedback await reader.beep(); } else { console.log('No card detected'); } } catch (error) { console.error('Error:', error); } finally { reader.closePort(); } } readCard(); ``` ### JavaScript (CommonJS) Example ```javascript const { MifareReader, COLORS } = require('mifare-reader-js'); const reader = new MifareReader(); async function main() { try { await reader.initialize('COM3', 19200); const cardId = await reader.selectCard(); if (cardId) { await reader.changeLedColor(COLORS.GREEN); await reader.beep(); const data = await reader.readCard(); console.log('Card data:', data); } } finally { reader.closePort(); } } main(); ``` ## API Reference ### MifareReader Class #### Constructor ```typescript const reader = new MifareReader(); ``` #### Methods ##### `initialize(portNo: string, baudRate: 9600 | 19200 | 57600 | 115200, maxRetries?: number): Promise<boolean>` Initializes connection to the card reader device. **Parameters:** - `portNo` - Serial port name - Windows: `'COM1'`, `'COM3'`, etc. - Linux/Mac: `'/dev/ttyUSB0'`, `'/dev/ttyACM0'`, etc. - `baudRate` - Communication speed (default: 19200) - Supported values: `9600`, `19200`, `57600`, `115200` - `maxRetries` - Maximum retry attempts (default: 20) **Returns:** Promise resolving to `true` if successful, `false` otherwise **Example:** ```typescript await reader.initialize('COM3', 19200); ``` --- ##### `selectCard(): Promise<string | null>` Attempts to detect and select a card in the reader. **Returns:** Promise resolving to: - Card ID as a hex string if card is detected - `null` if no card is detected **Example:** ```typescript const cardId = await reader.selectCard(); if (cardId) { console.log('Card ID:', cardId); } ``` --- ##### `readCard(key?: string | Buffer, keyMode?: 'A' | 'B'): Promise<string>` Reads data from the selected card. Requires successful authentication first. **Parameters:** - `key` - 6-byte authentication key (hex string or Buffer) - Default: `'ffffffffffff'` (default MIFARE key) - Must be 6 bytes/12 hex characters - `keyMode` - Authentication key mode - `'A'` (default) or `'B'` **Returns:** Promise resolving to card data as a hex string (16 bytes) **Note:** This method internally calls `authen()` for authentication. **Example:** ```typescript // Using default key const data = await reader.readCard(); // Using custom key with mode B const data = await reader.readCard('123456789abc', 'B'); ``` --- ##### `authen(key?: string | Buffer, keyMode?: 'A' | 'B'): Promise<boolean>` Authenticates with the card using the specified key. Must be called before `readCard()`. **Parameters:** - `key` - 6-byte authentication key (default: `'ffffffffffff'`) - `keyMode` - Key mode `'A'` or `'B'` (default: `'A'`) **Returns:** Promise resolving to `true` if authentication successful, `false` otherwise **Example:** ```typescript const authenticated = await reader.authen('ffffffffffff', 'A'); if (authenticated) { const data = await reader.readCard(); } ``` --- ##### `changeLedColor(color: 'GREEN' | 'RED' | 'YELLOW' | 'OFF'): Promise<boolean>` Controls the reader's LED indicator. **Parameters:** - `color` - LED color state - `'GREEN'` - Green LED - `'RED'` - Red LED - `'YELLOW'` - Yellow LED - `'OFF'` - Turn off LED **Returns:** Promise resolving to `true` if successful, `false` otherwise **Example:** ```typescript import { COLORS } from 'mifare-reader-js'; await reader.changeLedColor(COLORS.GREEN); ``` --- ##### `beep(): Promise<boolean>` Triggers the reader's audio indicator. **Returns:** Promise resolving to `true` if successful, `false` otherwise **Example:** ```typescript await reader.beep(); ``` --- ##### `closePort(): void` Closes the serial port connection. Should be called for proper cleanup. **Example:** ```typescript reader.closePort(); ``` --- ## Constants ### COLORS ```typescript import { COLORS } from 'mifare-reader-js'; console.log(COLORS.GREEN); // "GREEN" console.log(COLORS.RED); // "RED" console.log(COLORS.YELLOW); // "YELLOW" console.log(COLORS.OFF); // "OFF" ``` ### KEY_MODE ```typescript import { KEY_MODE } from 'mifare-reader-js'; console.log(KEY_MODE.A); // Buffer [0x61] console.log(KEY_MODE.B); // Buffer [0x60] ``` --- ## Port Configuration To find your serial port: ### Windows - Check Device Manager Ports (COM & LPT) - Common ports: `COM1`, `COM3`, `COM5` - Use `node -e "require('serialport').SerialPort.list().then(ports => console.log(ports))"` ### Linux ```bash # List all serial ports ls -la /dev/tty* # Common ports: /dev/ttyUSB0, /dev/ttyACM0 dmesg | grep tty # Check kernel messages ``` ### macOS ```bash # List all serial ports ls -la /dev/tty.* /dev/cu.* # Common ports: /dev/cu.usbserial-*, /dev/cu.SLAB_USBtoUART ``` --- ## Development Guide ### Prerequisites - Node.js >= 14.0.0 - npm or yarn - TypeScript knowledge (recommended) ### Setup 1. Clone the repository: ```bash git clone <repository-url> cd mifare-reader-js ``` 2. Install dependencies: ```bash npm install ``` 3. Build the project: ```bash npm run build ``` ### Project Structure ``` mifare-reader-js/ ├── src/ ├── core/ └── MifareReader/ └── index.ts # Main MifareReader class ├── types/ └── types.ts # Type definitions and constants ├── example-test-*.ts # Usage examples └── index.ts # Main entry point ├── dist/ ├── cjs/ # CommonJS compiled output └── esm/ # ES Module compiled output ├── scripts/ └── create-esm-package.js # ESM build helper ├── package.json ├── tsconfig.json # TypeScript config (CommonJS) ├── tsconfig.esm.json # TypeScript config (ES Modules) └── README.md ``` ### Build Scripts ```bash # Build both CommonJS and ES Module formats npm run build # Build only CommonJS npm run build:cjs # Build only ES Module npm run build:esm # Development mode (ts-node) npm run dev ``` ### Compilation Details #### CommonJS Build - Configuration: `tsconfig.json` - Output: `dist/cjs/` - Target: ES2020 - Module: CommonJS #### ES Module Build - Configuration: `tsconfig.esm.json` - Output: `dist/esm/` - Target: ES2020 - Module: ES2020 - Creates `package.json` with `"type": "module"` ### TypeScript Configuration The project uses strict TypeScript settings: - Strict mode enabled - Source maps included - Declaration files (.d.ts) generated ### Example Files Run the example files to test functionality: ```bash # Using ts-node directly npx ts-node src/example-test-readcard.ts # Or with npm script npm run dev src/example-test-readcard.ts ``` Available examples: - `example-test-readcard.ts` - Card detection and data reading - `example-test-led.ts` - LED color control - `example-test-beep.ts` - Audio feedback - `example-test-port.ts` - Basic port connectivity - `example-test-select-card.ts` - Card selection/detection - `example-test-mixing.ts` - Combined operations **Note:** Update the port and baud rate in examples before running. ### Making Changes 1. Modify source files in `src/` 2. Run `npm run build` to compile 3. Test changes with example files 4. Check compiled output in `dist/` directory ### Adding New Features 1. Add types to `src/types/types.ts` 2. Implement functionality in `src/core/MifareReader/index.ts` 3. Export new types/methods in `src/index.ts` 4. Create example file to demonstrate usage 5. Build and test with `npm run build` --- ## Publishing to npm 1. Create npm account at https://www.npmjs.com 2. Login locally: ```bash npm login ``` 3. Update `package.json` metadata: - Update version in `version` field - Add your username to `author` field - Update `repository.url`, `bugs.url`, and `homepage` 4. Publish: ```bash npm publish ``` The `prepublishOnly` script automatically builds before publishing. --- ## Troubleshooting ### Port Not Found - Verify the port name matches your system - Check Device Manager (Windows) or `ls /dev/tty*` (Linux/Mac) - Ensure the reader is properly connected and recognized by the OS ### Connection Fails - Try different baud rates (9600, 19200, 57600, 115200) - Verify the reader is powered and properly connected - Check for driver issues (Windows: USB drivers may need installation) - Increase `maxRetries` parameter ### Card Not Detected - Ensure card is properly positioned in the reader - Try moving the card slightly to find the sweet spot - Check if the reader's LED is functioning - Verify card is a MIFARE Classic card ### Authentication Fails - Verify the key is correct (should be 6 bytes/12 hex characters) - Try the default key `'ffffffffffff'` - Ensure key mode ('A' or 'B') matches the card configuration - Check card is still properly positioned ### Module Import Issues - For CommonJS: `const { MifareReader } = require('mifare-reader-js');` - For ES Modules: `import { MifareReader } from 'mifare-reader-js';` - Ensure build output exists: `npm run build` --- ## Changelog See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes and version updates. ### Latest Version (1.0.3) - **Fixed**: Select card ID issue - `selectCard()` now returns correct card ID --- ## License ISC --- ## Resources - [serialport Documentation](https://serialport.io/) - [TypeScript Documentation](https://www.typescriptlang.org/) --- ## Contributing Contributions are welcome! Please ensure: - Code follows TypeScript strict mode - Changes are properly tested - Documentation is updated - Build passes without errors: `npm run build`