记一次uniapp扫描NFC [ISO15693]

前炎

NFC的类型有很多种,详情:android NFC文档

这次的需求是读取NfcVISO15693类型的芯片

本次封装类本体来自uni-app NFC读取卡Id 扇门块的16进制数组 数据解析 (感谢大佬提供的封装)

本次需求原本是读取nfc的mac地址和从NEDF取数据,但后期考虑到NEDF可被读写就改成了读取指定块的数据,下面直接放代码

封装

/utils/NFCServices.js

js 复制代码
class NFCServices {
  readyWriteData = false; //开启写
  readyRead = false; //开启读
  noNFC = false;
  callBack = null;
  readyData = ''; //准备写入的数据
  static techListsArray = [
    ['android.nfc.tech.IsoDep'],
    ['android.nfc.tech.NfcA'],
    ['android.nfc.tech.NfcB'],
    ['android.nfc.tech.NfcF'],
    ['android.nfc.tech.Nfcf'],
    ['android.nfc.tech.NfcV'],
    ['android.nfc.tech.NdefFormatable'],
    ['android.nfc.tech.MifareClassic'],
    ['android.nfc.tech.MifareUltralight']
  ];
  static package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
  static READ_SUCCESS = '1' //读取成功
  static READ_FAIL = '2' //读取失败
  static READING = '3' //正在读取
  static WRITING = '4' //正在写入
  static WRITE_SUCCESS = '5' //写入成功
  static WRITE_FAIL = '6' //写入失败
  static DEVICE_NO_NFC = '7' //设备不支持NFC
  static DEVICE_NFC_NOT_ENABLE = '8' //设备未开启NFC
  static DEVICE_NFC_NOT_ENABLE = '9' //设备未开启NFC
  static INIT = '10' //初始化NFC
  static INIT_ERROR = '11' //初始化NFC失败

  constructor(callBack, continuousRead) {
    //是否连续读取NFC数据
    this.isContinuousRead = continuousRead !== undefined ? continuousRead : true

    this.callBack = callBack
    this.RuntimeMainActivity = plus.android.runtimeMainActivity();
    this.Intent = plus.android.importClass('android.content.Intent');
    this.PendingIntent = plus.android.importClass('android.app.PendingIntent');
    this.IntentFilter = plus.android.importClass('android.content.IntentFilter');
    this.NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
    this.NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
    this.NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
    this.Ndef = plus.android.importClass('android.nfc.tech.Ndef');
    this.NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
    this.MifareClassic = plus.android.importClass('android.nfc.tech.MifareClassic');
    this.nfcA = plus.android.importClass('android.nfc.tech.NfcA')
    this.NfcV = plus.android.importClass('android.nfc.tech.NfcV')
    // this.Parcelable = plus.android.importClass("android.os.Parcelable");
  }
  // 将字符串切割成每两个字符一组,然后反转数组,最后拼接成字符串
  reverseString(inputString) {
    return inputString.match(/.{1,2}/g).reverse().join('');
  }
  // 字符串转mac
  stringToMacAddress(inputString) {
    // 如果是真货长度是16位,mac取反
    if (inputString.length === 16) {
      inputString = this.reverseString(inputString)    }
    // 删除字符串中的非十六进制字符
    var cleanedString = inputString.replace(/[^0-9A-Fa-f]/g, '');
    // 将字符串切割成每两个字符一组
    var pairs = cleanedString.match(/.{1,2}/g);
    // 使用冒号连接数组中的每一组字符
    var macAddress = pairs.join(':').toUpperCase();
    return macAddress;
  }

  listenNFCStatus() {
    if (uni.getSystemInfoSync().platform !== "android") {
      return
    }
    this.callBack(NFCServices.INIT, '监听NFC状态!')
    try {
      let nfcAdapter = this.NfcAdapter.getDefaultAdapter(this.RuntimeMainActivity);

      if (nfcAdapter == null) {
        this.callBack(NFCServices.DEVICE_NO_NFC, '设备不支持NFC!')
        this.noNFC = true;
        return;
      }

      if (!nfcAdapter.isEnabled()) {
        this.callBack(NFCServices.DEVICE_NFC_NOT_ENABLE, '请在系统设置中先启用NFC功能!')
        this.noNFC = true;
        return;
      } else {
        this.noNFC = false;
      }

      let intent = new this.Intent(this.RuntimeMainActivity, this.RuntimeMainActivity.getClass());
      intent.addFlags(this.Intent.FLAG_ACTIVITY_SINGLE_TOP);
      let pendingIntent = this.PendingIntent.getActivity(this.RuntimeMainActivity, 0, intent, 0);
      let ndef = new this.IntentFilter("android.nfc.action.TECH_DISCOVERED");
      ndef.addDataType("*/*");
      let intentFiltersArray = [ndef];
      const that = this
      plus.globalEvent.addEventListener('newintent', () => {
        console.log('newintent running');
        // 轮询调用 NFC
        that.nfcRuning()
        // setTimeout(, 1000);
      });

      plus.globalEvent.addEventListener('pause', (e) => {
        console.log('pause running');
        if (nfcAdapter) {
          //关闭前台调度系统 恢复默认状态
          nfcAdapter.disableForegroundDispatch(this.RuntimeMainActivity);
        }
      });
      plus.globalEvent.addEventListener('resume', (e) => {
        if (nfcAdapter) {
          //开启前台调度系统 优于所有其他NFC
          nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent,
            intentFiltersArray,
            NFCServices.techListsArray);
        }
      });

      nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent, intentFiltersArray,
        NFCServices.techListsArray);
    } catch (e) {
      this.callBack(NFCServices.INIT_ERROR, '初始化NFC失败!', e)

    }
  }
  nfcRuning() { //
    let intent = this.RuntimeMainActivity.getIntent();
    if (NFCServices.package_TECH_DISCOVERED == intent.getAction()) {
      if (this.isContinuousRead || this.readyRead) {
        this.read(intent);
        this.readyRead = false;
      }
    }
  }
  // 拼接两个块的数据,返回结果是Byte数组或者null
  readBlockConcat(nfcV, tagUid, index = 0x24) {
    // 0x20: Read Single Block 
    let buffer = [0x22, 0x20, ...tagUid, index];
    return nfcV.transceive(buffer);
  }
  // 将十六进制字符串每两位分割成一个数组
  hexStringToArrayAndConvert(hexString) {
    let hexArray = hexString.match(/.{1,2}/g) || [];
    // 将每个元素转换为十进制数
    let decimalArray = hexArray.map(hex => parseInt(hex, 16));
    return decimalArray;
  }

  // 将每个十进制数值转换为对应的 ASCII 数值
  decimalArrayToAscii(decimalArray) {
    let asciiArray = decimalArray.map(decimal => String.fromCharCode(decimal));
    return asciiArray;
  }

  read(intent) { // 读代码
    this.callBack(NFCServices.READING, '请勿移开标签正在读取数据!')
    // NFC id
    let bytesId = intent.getByteArrayExtra(this.NfcAdapter.EXTRA_ID);
    let nfc_id = this.byteArrayToHexString(bytesId);
    let extractedValues = {}
    let tag = plus.android.importClass('android.nfc.Tag');
    tag = intent.getParcelableExtra(this.NfcAdapter.EXTRA_TAG);
    // 开始读取扇区
    let NfcV = this.NfcV
    // 此处地址有长度判断
    if (nfc_id.length === 16) {
      let tech = NfcV.get(tag);
      if (tech != null) {
        try {
          tech.connect();
          let isConnected = tech.isConnected()
          if (isConnected) {
          // 感觉这个tagUid就是上面的nfc_id (mac地址)
            var tagUid = tag.getId();
            //要取0x24 0x25行的数据(36 37)
            let line24 = this.readBlockConcat(tech, tagUid, 0x24)
            let line25 = this.readBlockConcat(tech, tagUid, 0x25)
            if (line24 && line25) {
              let str = String(this.byteArrayToHexString(line24) + this.byteArrayToHexString(line25)).slice(4);

              // 转换为 ASCII 数组,然后连接成字符串
              let statusArr = this.decimalArrayToAscii(this.hexStringToArrayAndConvert(str));
              let openStatus = statusArr.length ? statusArr.join('') : 'EEEEEEEE';

              extractedValues = {
                read_raw: nfc_id,
                mac_raw: this.reverseString(nfc_id),
                mac: this.stringToMacAddress(nfc_id),
                openStatus: openStatus
              }
              // console.log(extractedValues);
              this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!', extractedValues)

            }
          }
        } catch (e) {
          console.log('nfc catch', e);
          this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
        }
      }
    } else {
      //mac地址长度不符合本次需求,返回mac地址和一个错误的openStatus,openStatus看自己业务情况,可以去掉
      extractedValues = {
        read_raw: nfc_id,
        mac_raw: this.reverseString(nfc_id),
        mac: this.stringToMacAddress(nfc_id),
        openStatus: 'EEEEEEEE'
      }
      this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!', extractedValues)
      // console.log(extractedValues);
    }

    // 以下注释为NEDF(payload message)
    // if (nfc_id) {
    //   let Parcelable = plus.android.importClass("android.os.Parcelable");
    //   let rawmsgs = intent.getParcelableArrayExtra("android.nfc.extra.NDEF_MESSAGES");
    //   //let rawmsgs = intent.getParcelableArrayExtra();

    //   if (rawmsgs != null && rawmsgs.length > 0) {
    //     let records = rawmsgs[0].getRecords();
    //     let result = records[0].getPayload();
    //     let data = plus.android.newObject("java.lang.String", result);
    //     // 因为此NFC芯片 nedf数据 是个网址,读取多了个\u0004,按自己情况处理
    //     let info = data.replace('\u0004', '')
    //     let extractedValues =  {
    // 			data:info ,
    // 			mac:this.stringToMacAddress(nfc_id),
    //		}
    //     // callBack数据按照自己需求来返回
    //     this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!',extractedValues)
    //   } else {
    //     console.log('没有读取到数据');
    //     this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
    //   }
    // } else {
    //   this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
    // }

    return nfc_id
  }

  // 将字节数组转换为字符串  
  byteArrayToHexString(inarray) {
    let i, j, inn;
    let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
    let out = "";

    for (j = 0; j < inarray.length; ++j) {
      inn = inarray[j] & 0xff;
      i = (inn >>> 4) & 0x0f;
      out += hex[i];
      i = inn & 0x0f;
      out += hex[i];
    }
    return out;
  }
  readData() { // 更改读状态
    if (this.noNFC) {
      this.callBack(NFCServices.READ_FAIL, '请检查设备是否支持并开启 NFC 功能!')

      return;
    }
    // 轮询条件
    this.readyRead = true;
    this.callBack(NFCServices.READING, '请将NFC标签靠近!')

  }
  // 设置数据
  destroy() {
    console.log('destroy');
    plus.globalEvent.removeEventListener('newintent', false);
    plus.globalEvent.removeEventListener('pause', false);
    plus.globalEvent.removeEventListener('resume', false);
  }
}
export default NFCServices

附扇区数据截图:

安卓端可以使用 NFC标签助手@1.3.4 [com.fm.nfctools] 以及NFC Tools@8.10 [com.wakdev.wdnfc]读取NFC信息

下载地址:https://wwm.lanzn.com/b05exoigb 密码:aiwp或者扫码

使用

/pages/out-scan/index.vue

html 复制代码
<template>
	<view>
		<view @click="read">读取NFC数据</view>
	</view>
</template>
<script>
  import NFCServices from '@/utils/nfcServices.js'
  let nfcServices;
  export default {
    data() {
      return {}
    },
    onLoad() {
      this.initNfc()
    },
    onUnload() {
      nfcServices&&nfcServices.destroy()
    },
    methods: {
      NFCServicesCallBack(status, tip, data) {
        switch (status) {
          case NFCServices.READ_SUCCESS:
            // 成功回调,就是callback里写的对象,如果是NEDF则是callback里面定义的结构数据
            console.log(data)
            break;
            case NFCServices.READ_FAIL:
               // 错误回调
               console.error('错误回调',data)
              break;
          default:
            break;
        }   
      },
      initNfc() {
        nfcServices = new NFCServices(this.NFCServicesCallBack)
      },
      // 取消NFC监听
      cancel() {
        // console.log('cancel');
        nfcServices.destroy()
        nfcServices = null
      },
      read() {
        if (!nfcServices) {
          nfcServices = new NFCServices(this.NFCServicesCallBack)
          nfcServices.listenNFCStatus()
        }else{
          nfcServices.listenNFCStatus()
        }
        uni.showToast({
          title: "请将NFC标签靠近",
          icon: "none"
        })
        // 调用 js 文件里面的方法
        nfcServices.readData()
      },
    }
  }
</script>
 

注意:

getid()方法在android.nfc.Tag里,不在NfcV的文档里面

相关推荐
阿伟来咯~3 小时前
一些 uniapp相关bug
uni-app·bug
瑶琴AI前端7 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
mosen8687 小时前
Uniapp去除顶部导航栏-小程序、H5、APP适用
vue.js·微信小程序·小程序·uni-app·uniapp
尚梦15 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
尚学教辅学习资料1 天前
基于SSM+uniapp的营养食谱系统+LW参考示例
java·uni-app·ssm·菜谱
Bessie2341 天前
微信小程序eval无法使用的替代方案
微信小程序·小程序·uni-app
qq22951165021 天前
小程序Android系统 校园二手物品交换平台APP
微信小程序·uni-app
qq22951165022 天前
微信小程序uniapp基于Android的流浪动物管理系统 70c3u
微信小程序·uni-app
qq22951165022 天前
微信小程序 uniapp+vue老年人身体监测系统 acyux
vue.js·微信小程序·uni-app
摇头的金丝猴2 天前
uniapp vue3 使用echarts-gl 绘画3d图表
前端·uni-app·echarts