UNPKG

obniz

Version:

obniz sdk for javascript

574 lines (573 loc) 24.2 kB
"use strict"; /** * @packageDocumentation * @module Parts.MFRC522 */ Object.defineProperty(exports, "__esModule", { value: true }); const OK = true; const ERROR = false; class MFRC522 { constructor() { // PCD commands. Described in chapter 10 of the datasheet. // PCD(Proximity Coupling Device): NXP MFRC522 Contactless Reader IC. this.PCD_Idle = 0x00; // no action, cancels current command execution. this.PCD_Mem = 0x01; // stores 25 bytes into the internal buffer. this.PCD_GenerateRandomID = 0x02; // generates a 10-byte random ID number. this.PCD_CalcCRC = 0x03; // activates the CRC coprocessor or performs a self-test. this.PCD_Transmit = 0x04; // transmits data from the FIFO buffer. this.PCD_NoCmdChange = 0x07; // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit. this.PCD_Receive = 0x08; // activates the receiver circuits. this.PCD_Transceive = 0x0c; // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission. // this.PCD_Reserved0Dh = 0x0D; this.PCD_MFAuthent = 0x0e; // performs the MIFARE standard authentication as a reader. this.PCD_SoftReset = 0x0f; // resets the MFRC522. // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf this.RxGain_18dB = 0x00 << 4; // 000b - 18 dB, minimum. this.RxGain_23dB = 0x01 << 4; // 001b - 23 dB. this.RxGain_18dB_2 = 0x02 << 4; // 010b - 18 dB, it seems 010b is a duplicate for 000b. this.RxGain_23dB_2 = 0x03 << 4; // 011b - 23 dB, it seems 011b is a duplicate for 001b. this.RxGain_33dB = 0x04 << 4; // 100b - 33 dB, average, and typical default. this.RxGain_38dB = 0x05 << 4; // 101b - 38 dB. this.RxGain_43dB = 0x06 << 4; // 110b - 43 dB. this.RxGain_48dB = 0x07 << 4; // 111b - 48 dB, maximum. this.RxGain_min = 0x00 << 4; // 000b - 18 dB, minimum, convenience for RxGain_18dB. this.RxGain_avg = 0x04 << 4; // 100b - 33 dB, average, convenience for RxGain_33dB. this.RxGain_max = 0x07 << 4; // 111b - 48 dB, maximum, convenience for RxGain_48dB. // The PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4). this.PICC_REQA = 0x26; // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. this.PICC_WUPA = 0x52; // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. this.PICC_CT = 0x88; // Cascade Tag. Not really a command, but used during anti collision. this.PICC_SEL_CL1 = 0x93; // Anti collision/Select, Cascade Level 1. this.PICC_SEL_CL2 = 0x95; // Anti collision/Select, Cascade Level 2. this.PICC_SEL_CL3 = 0x97; // Anti collision/Select, Cascade Level 3. this.PICC_HLTA = 0x50; // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. this.PICC_RATS = 0xe0; // Request command for Answer To Reset. // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. // The read/write commands can also be used for MIFARE Ultralight. this.PICC_AUTH_KEYA = 0x60; // Perform authentication with Key A. this.PICC_AUTH_KEYB = 0x61; // Perform authentication with Key B. this.PICC_READ = 0x30; // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. this.PICC_WRITE = 0xa0; // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. this.PICC_DECREMENT = 0xc0; // Decrements the contents of a block and stores the result in the internal data register. this.PICC_INCREMENT = 0xc1; // Increments the contents of a block and stores the result in the internal data register. this.PICC_RESTORE = 0xc2; // Reads the contents of a block into the internal data register. this.PICC_TRANSFER = 0xb0; // Writes the contents of the internal data register to a block. // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. this.PICC_UL_WRITE = 0xa2; // Writes one 4 byte page to the PICC. this.PICC_SElECTTAG = 0x93; // Page 0: Command and status // this.Reserved00h = 0x00; this.CommandReg = 0x01; this.ComlEnReg = 0x02; this.DivlEnReg = 0x03; this.ComIrqReg = 0x04; this.DivIrqReg = 0x05; this.ErrorReg = 0x06; this.Status1Reg = 0x07; this.Status2Reg = 0x08; this.FIFODataReg = 0x09; this.FIFOLevelReg = 0x0a; this.WaterLevelReg = 0x0b; this.ControlReg = 0x0c; this.BitFramingReg = 0x0d; this.CollReg = 0x0e; // this.Reserved0Fh = 0x0F; // Page 1: Command // this.Reserved10h = 0x10; this.ModeReg = 0x11; this.TxModeReg = 0x12; this.RxModeReg = 0x13; this.TxControlReg = 0x14; this.TxASKReg = 0x15; this.TxSelReg = 0x16; this.RxSelReg = 0x17; this.RxThresholdReg = 0x18; this.DemodReg = 0x19; this.Reserved1Ah = 0x1a; this.Reserved1Bh = 0x1b; this.MfTxReg = 0x1c; this.MfRxReg = 0x1d; this.Reserved1Eh = 0x1e; this.SerialSpeedReg = 0x1f; // Page 2: Configuration // this.Reserved20h = 0x20; this.CRCResultRegMSB = 0x21; this.CRCResultRegLSB = 0x22; // this.Reserved23h = 0x23; this.ModWidthReg = 0x24; // this.Reserved25h = 0x25; this.RFCfgReg = 0x26; this.GsNReg = 0x27; this.CWGsPReg = 0x28; this.ModGsPReg = 0x29; this.TModeReg = 0x2a; this.TPrescalerReg = 0x2b; this.TReloadRegHi = 0x2c; this.TReloadRegLo = 0x2d; this.TCounterValRegHi = 0x2e; this.TCounterValRegLo = 0x2f; // Page 3: Test register // this.Reserved30h = 0x30; this.TestSel1Reg = 0x31; this.TestSel2Reg = 0x32; this.TestPinEnReg = 0x33; this.TestPinValueReg = 0x34; this.TestBusReg = 0x35; this.AutoTestReg = 0x36; this.VersionReg = 0x37; this.AnalogTestReg = 0x38; this.TestDAC1Reg = 0x39; this.TestDAC2Reg = 0x3a; this.TestADCReg = 0x3b; // this.Reserved3Ch = 0x3C; // this.Reserved3Dh = 0x3D; // this.Reserved3Eh = 0x3E; // this.Reserved3Fh = 0x3F; // required pin of obniz this.keys = [ 'cs', 'clk', 'mosi', 'miso', 'rst', 'vcc', 'gnd', 'spi', 'spi_frequency', ]; this.requiredKeys = ['cs', 'mosi', 'miso', 'rst']; } static info() { return { name: 'MFRC522', }; } wired(obniz) { this.obniz = obniz; // IO pin settings this.obniz.setVccGnd(this.params.vcc, this.params.gnd, '5v'); this.rst = obniz.getIO(this.params.rst); // SPI settings this.cs = obniz.getIO(this.params.cs); this.cs.output(true); this.params.mode = 'master'; this.params.drive = '3v'; this.params.pull = '3v'; this.params.frequency = this.params.spi_frequency || 5 * 1000 * 1000; this.spi = this.obniz.getSpiWithConfig(this.params); } /** * @deprecated */ init() { return this.initWait(); } async initWait() { // Initializes the MFRC522 chip // Hardware and Software reset this.rst.output(false); await this.obniz.wait(50); // 8.8.2 says the oscillator start-up time is the start up time of the crystal + 37,74us: 50ms. this.rst.output(true); this.writeRegister(this.CommandReg, this.PCD_SoftReset); // Timer setup: When communicating with a PICC we need a timeout if something goes wrong. // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. this.writeRegister(this.TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds this.writeRegister(this.TPrescalerReg, 0xa9); // TPreScaler = TModeReg[3..0]: TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25us. this.writeRegister(this.TReloadRegHi, 0x03); this.writeRegister(this.TReloadRegLo, 0xe8); // Reload timer with 0x3E8 = 1000, ie. 25ms before timeout this.writeRegister(this.TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting this.writeRegister(this.ModeReg, 0x3d); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (6.2.4) await this.antennaOnWait(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) } writeRegister(addr, val) { let data; if (val instanceof Array) { // If val is Array data = [(addr << 1) & 0x7e].concat(val); } else { data = [(addr << 1) & 0x7e, val]; } this.cs.output(false); this.spi.write(data); this.cs.output(true); } /** * @deprecated * @param addr */ readRegister(addr) { return this.readRegisterWait(addr); } async readRegisterWait(addr) { const data = [((addr << 1) & 0x7e) | 0x80, 0]; this.cs.output(false); const response = await this.spi.writeWait(data); this.cs.output(true); return response[1]; } /** * @deprecated * @param addr * @param n */ readRegister_nByte(addr, n) { return this.readRegister_nByteWait(addr, n); } async readRegister_nByteWait(addr, n) { const dataArray = []; if (addr instanceof Array) { // Multiple addresses(If addr is Array) for (let i = 0; i < addr.length; i++) { dataArray.push(((addr[i] << 1) & 0x7e) | 0x80); } } else { // Single address & read n times for (let i = 0; i < n; i++) { dataArray.push(((addr << 1) & 0x7e) | 0x80); } } dataArray.push(0); // End reading this.cs.output(false); const values = await this.spi.writeWait(dataArray); this.cs.output(true); values.shift(); return values; } /** * @deprecated * @param reg * @param mask */ setRegisterBitMask(reg, mask) { return this.setRegisterBitMaskWait(reg, mask); } async setRegisterBitMaskWait(reg, mask) { const response = await this.readRegisterWait(reg); this.writeRegister(reg, response | mask); } /** * @deprecated * * @param reg * @param mask */ clearRegisterBitMask(reg, mask) { return this.clearRegisterBitMaskWait(reg, mask); } async clearRegisterBitMaskWait(reg, mask) { const response = await this.readRegisterWait(reg); this.writeRegister(reg, response & ~mask); } /** * @deprecated */ antennaOn() { return this.antennaOnWait(); } async antennaOnWait() { // Turns the antenna on by enabling pins TX1 and TX2 const response = await this.readRegisterWait(this.TxControlReg); if ((response & 0x03) !== 0x03) { // If TX1 and TX2 down await this.setRegisterBitMaskWait(this.TxControlReg, response | 0x03); } } /** * @deprecated */ antennaOff() { return this.antennaOffWait(); } async antennaOffWait() { // Turns the antenna off by disabling pins TX1 and TX2 await this.clearRegisterBitMaskWait(this.TxControlReg, 0x03); } /** * @deprecated * @param command * @param bitsToSend */ toCard(command, bitsToSend) { return this.toCardWait(command, bitsToSend); } // RC522 and ISO14443 card communication async toCardWait(command, bitsToSend) { let data = []; let bitSize = 0; let status = ERROR; let irqEn = 0x00; let waitIRq = 0x00; if (command === this.PCD_MFAuthent) { irqEn = 0x12; waitIRq = 0x10; } if (command === this.PCD_Transceive) { irqEn = 0x77; waitIRq = 0x30; } this.writeRegister(this.CommandReg, this.PCD_Idle); // Stop any active command this.writeRegister(this.ComlEnReg, irqEn | 0x80); // Interrupt request is enabled this.writeRegister(this.ComIrqReg, 0x7f); // Clear all seven interrupt request bits this.writeRegister(this.FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization this.writeRegister(this.FIFODataReg, bitsToSend); // Write sendData to the FIFO this.writeRegister(this.CommandReg, command); // Execute the command if (command === this.PCD_Transceive) { await this.setRegisterBitMaskWait(this.BitFramingReg, 0x80); // StartSend=1, transmission of data starts } let TryingTimes = 10; let n = 0; do { // Wait for the received data complete n = await this.readRegisterWait(this.ComIrqReg); TryingTimes--; } while (TryingTimes !== 0 && !(n & 0x01) && !(n & waitIRq)); // !(Timer interrupt - nothing received before timeout) & !(One of the interrupts that signal success has been set) // await this.clearRegisterBitMaskWait(this.BitFramingReg, 0x80); //Reset with resetAndInit() const response = await this.readRegister_nByteWait([ this.ErrorReg, this.FIFOLevelReg, this.ControlReg, ]); if (TryingTimes !== 0) { if ((response[0] & 0x1b) === 0x00) { // BufferOvfl CollErr ParityErr ProtocolErr status = n & irqEn & 0x01 ? ERROR : OK; if (command === this.PCD_Transceive) { n = response[1]; // Number of bytes in the FIFO const lastBits = response[2] & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. if (lastBits) { bitSize = (n - 1) * 8 + lastBits; } else { bitSize = n * 8; } if (n === 0) { n = 1; } if (n > 16) { n = 16; } // Restrict until 16bytes data = await this.readRegister_nByteWait(this.FIFODataReg, n); // Get received data from FIFO buffer } } else { status = ERROR; } } return { status, data, bitSize }; } async findCardWait() { await this.initWait(); await this.searchTagWait(); const uid = await this.getUidWait(); const PICC_Type = await this.identifyCardTypeWait(uid); return { uid, PICC_Type }; } async searchTagWait() { this.writeRegister(this.BitFramingReg, 0x07); const tagType = [this.PICC_REQA]; const response = await this.toCardWait(this.PCD_Transceive, tagType); if (response.bitSize !== 0x10) { throw new Error('card_search_ERROR'); } } async getUidWait() { this.writeRegister(this.BitFramingReg, 0x00); let uid = [this.PICC_SEL_CL1, 0x20]; const response = await this.toCardWait(this.PCD_Transceive, uid); if (!response.status) { throw new Error('uid_scan_ERROR'); } const uidCheck = response.data[0] ^ response.data[1] ^ response.data[2] ^ response.data[3]; if (uidCheck !== response.data[4]) { throw new Error('uid_check_ERROR'); } uid = response.data; // (uid).pop(); return uid; } async calculateCRCWait(data) { this.writeRegister(this.CommandReg, this.PCD_Idle); // Stop any active command this.writeRegister(this.DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit this.writeRegister(this.FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization this.writeRegister(this.FIFODataReg, data); // Write data to the FIFO this.writeRegister(this.CommandReg, this.PCD_CalcCRC); // Start the calculation let i = 0xff; let n; // Wait for the CRC calculation to complete do { n = await this.readRegisterWait(this.DivIrqReg); i--; } while (i !== 0 && !(n & 0x04)); // CRCIrq = 1 (Calculation done) // CRC calculation result return await this.readRegister_nByteWait([ this.CRCResultRegLSB, this.CRCResultRegMSB, ]); } async identifySoftwareWait() { let version = await this.readRegisterWait(this.VersionReg); switch (version) { case 0x88: version = '(clone)'; break; case 0x90: version = 'v0.0'; break; case 0x91: version = 'v1.0'; break; case 0x92: version = 'v2.0'; break; case 0x12: version = 'counterfeit chip'; break; default: version = '(unknown)'; } // When 0x00 or 0xFF is returned, communication probably failed if (version === 0x00 || version === 0xff) { throw new Error('software_version_ERROR'); } return version; } async identifyCardTypeWait(uid) { // Identify type of the scanned card let buffer = [this.PICC_SElECTTAG, 0x70].concat(uid); buffer = buffer.concat(await this.calculateCRCWait(buffer)); const response = await this.toCardWait(this.PCD_Transceive, buffer); let PICC_Type; if (response.status && response.bitSize === 0x18) { PICC_Type = response.data[0]; } switch (PICC_Type) { case 0x04: PICC_Type = 'SAK indicates UID is not complete.'; break; // UID not complete case 0x09: PICC_Type = 'MIFARE Mini, 320 bytes'; break; case 0x08: PICC_Type = 'MIFARE 1KB'; break; case 0x18: PICC_Type = 'MIFARE 4KB'; break; case 0x00: PICC_Type = 'MIFARE Ultralight or Ultralight C'; break; case 0x11: PICC_Type = 'MIFARE Plus'; break; case 0x01: PICC_Type = 'MIFARE TNP3XXX'; break; case 0x20: PICC_Type = 'PICC compliant with ISO/IEC 14443-4'; break; case 0x40: PICC_Type = 'PICC compliant with ISO/IEC 18092 (NFC)'; break; default: throw new Error('PICC_type_ERROR'); } return PICC_Type; } async readSectorDataWait(Sector, uid) { await this.authenticateSectorWait(Sector, uid); return await this.getSectorDataWait(Sector); } async readBlockDataWait(Block, uid) { await this.authenticateBlockWait(Block, uid); return await this.getBlockDataWait(Block); } async authenticateSectorWait(Sector, uid) { /* Password authentication mode (A or B) * PICC_AUTH_KEYA = Verify the A key are the first 6 bit of 4th Block of each sector * PICC_AUTH_KEYB = Verify the B key are the last 6 bit of 4th Block of each sector */ const KEY_A = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; // const KEY_B = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; const Block = Sector * 4; let buffer = [this.PICC_AUTH_KEYA, Block].concat(KEY_A); // Append key = 6 bit of 0xFF uid = uid.slice(0, 4); // Append the first 4 bit of the UID buffer = buffer.concat(uid); // 12byte // Start authentication itself await this.toCardWait(this.PCD_MFAuthent, buffer); if (!((await this.readRegisterWait(this.Status2Reg)) & 0x08)) { throw new Error('password_authentication_ERROR'); } } async authenticateBlockWait(Block, uid) { /* Password authentication mode (A or B) * PICC_AUTH_KEYA = Verify the A key (the first 6 bit of 3th Block fo each Sector) * PICC_AUTH_KEYB = Verify the B key (the last 6 bit of 3th Block fo each Sector) */ const KEY_A = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; // const KEY_B = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; let buffer = [this.PICC_AUTH_KEYA, Block].concat(KEY_A); // Append key = 6 bit of 0xFF uid = uid.slice(0, 4); // Append the first 4 bit of the UID buffer = buffer.concat(uid); // 12byte // Start authentication itself await this.toCardWait(this.PCD_MFAuthent, buffer); if (!((await this.readRegisterWait(this.Status2Reg)) & 0x08)) { throw new Error('password_authentication_ERROR'); } } async readAgainWait() { // If you finish reading and want to read again, this can use instead of initWait() await this.clearRegisterBitMaskWait(this.Status2Reg, 0x08); } async getSectorDataWait(address) { const response = []; const blockData = []; for (let i = 0; i < 4; i++) { let request = [this.PICC_READ, address * 4 + i]; request = request.concat(await this.calculateCRCWait(request)); response[i] = await this.toCardWait(this.PCD_Transceive, request); if (!response[i].status) { throw new Error('data_read_ERROR'); } blockData[i] = response[i].data; } return blockData; } async getBlockDataWait(address) { let request = [this.PICC_READ, address]; request = request.concat(await this.calculateCRCWait(request)); const response = await this.toCardWait(this.PCD_Transceive, request); if (!response.status) { throw new Error('data_read_ERROR'); } return response.data; } async appendCRCtoBufferAndSendToCardWait(buffer) { buffer = buffer.concat(await this.calculateCRCWait(buffer)); const response = await this.toCardWait(this.PCD_Transceive, buffer); if (!response.status || response.bitSize !== 4 || (response.data[0] & 0x0f) !== 0x0a) { response.status = ERROR; } return response; } async writeBlockDataWait(Block, sixteenBytes) { if (Block === 0 || Block % 4 === 3) { throw new Error('deny_Write'); } const buffer = [this.PICC_WRITE, Block]; let response = await this.appendCRCtoBufferAndSendToCardWait(buffer); if (response.status) { response = await this.appendCRCtoBufferAndSendToCardWait(sixteenBytes); } else { throw new Error('data_write_ERROR'); } } } exports.default = MFRC522;