基于JNA(Java Native Access)实现RFID单卡、多卡读取以及写入

前言

在对RFID等硬件操作中,会使用C或C++进行实现,但如果是基于Java环境进行开发,就会和C++、C进行交互,调用底层的一些方法。在JNA出现之前,使用JNI会存在封装性不好、不稳定等问题。

JNA全称Java Native Access,是一个建立在JNI技术之上的Java开源框架。JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll,Linux的so)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。

硬件

使用协议: RFID ISO15693协议; 读写器: 高频15693 RFID读写器; 电子标签: 高频15693 图书电子标签。

动态库

OUR_MIFARE.dll
1、不同版本的操作系统及读写器,接口DLL文件名称、函数名称是一样的 2、根据您的读写器、操作系统版本,选择相应目录内的 OUR_MIFARE.dll 文件拷贝覆盖到 class文件对应的输出目录,如:out\production\Java_Jna_15693Demo\ 目录 或 maven项目的 target\classes\com\你代码对应的文件夹 内就可以调试运行。 3、不要使用中文目录

jar依赖

jna.4.5.5.jar

1. RFID单卡、多卡读取卡序列号

import com.sun.jna.Library;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lin
 * @Date 2021/5/9 10:57
 */
interface MyLibrary extends Library {
    //DLL绝对路径的地址获取,注意要去空格,特别坑
    //不同版本的读写器,接口DLL文件名称、函数名称是一样的,但内核代码不一样,请选用与读写器、操作系统一致的OUR_MIFARE.dll
    String filePath = MyLibrary.class.getResource("").getPath().replaceFirst("/","").replaceAll("%20"," ")+"OUR_MIFARE";
    MyLibrary sdtapi = (MyLibrary) Native.loadLibrary(filePath, MyLibrary.class);

    //动态链接库中的方法
    byte pcdbeep(int xms);                         //让设备发出声音
    byte iso15693readex(byte ctrlword,byte afi,byte startblock,byte blocknum,byte[] mypiccserial,byte[] piccdata);    //轻松读卡,读出卡号及从某块开始的N块信息
    byte iso15693inventory16(byte var1, byte var2, byte var3, byte[] var4, byte[] var5, byte[] var6);
}
@Component
@Slf4j
public class MyRFID {

    public String RFIDReader() throws InterruptedException {
        System.setProperty("jna.encoding", "GBK");
        int status;                       //存放返回值
        byte myctrlword = 0;              //读写控制字,一般取0 就行了
        byte afi = 0;                     //卡片应用标识
        byte startblock = 0;              //指定读起始块地址,最大取值255(根据卡类型)
        byte blocknum = 12;               //指定读取块数,最大取值12
        byte[] myuid = new byte[8];       //8字节卡序列号缓冲
        byte[] mydatabuf = new byte[255]; //读卡数据缓冲共255个字节

        status = (int) (MyLibrary.sdtapi.iso15693readex(myctrlword, afi, startblock, blocknum, myuid, mydatabuf) & 0xff);
        String serialnumber = "";
        if (status == 0) {
            MyLibrary.sdtapi.pcdbeep(38);
            for (int i = 0; i < 8; i++) {
                String bytestr = "00" + Integer.toHexString(myuid[i] & 0xff);
                serialnumber = serialnumber + bytestr.substring(bytestr.length() - 2, bytestr.length());
            }
           log.info("读卡成功,卡序列号:{}" ,serialnumber );
           // 读取卡内数据
           /* for(j = 1; j < mydatabuf[0] + 1; ++j) {
                 bytestr = "00" + Integer.toHexString(mydatabuf[j] & 255);
                 databuf = databuf + bytestr.substring(bytestr.length() - 2, bytestr.length()) + " ";
            }

         System.out.print("卡内数据:" + databuf + "\n"); */
        }
        return serialnumber;
    }

    public List<String> RFIDReadCards(){
        byte flags = 0;
        byte startblock = 22;
        byte blocknum = 0;
        byte[] myuid = new byte[8];
        byte[] revlen = new byte[81];
        byte[] mydatabuf = new byte[1];
        String databuf;
        List<String> list = new ArrayList<>();
        log.info("\n5-搜寻感应区内多张15693卡\n");
        int status = MyLibrary.sdtapi.iso15693inventory16(startblock, flags, blocknum, myuid, mydatabuf, revlen) & 255;
        if (status == 0) {
            MyLibrary.sdtapi.pcdbeep(38);
            for(int j = 0; j < mydatabuf[0]; j += 9) {
                String serialnumber = "";

                for(int i = 1; i < 9; ++i) {
                    databuf = "00" + Integer.toHexString(revlen[j + i] & 255);
                    serialnumber = serialnumber + databuf.substring(databuf.length() - 2, databuf.length());
                }
                 list.add(serialnumber);
                log.info("卡序列号UID:{}" ,serialnumber );
            }
        }
        return list;
    }


}

2、RFID读取

读取从某块开始的N块信息

		    int status;                       //存放返回值
            byte myctrlword = 0;              //读写控制字,一般取0 就行了
            byte afi = 0;                     //卡片应用标识
            byte startblock = 0;              //指定读起始块地址,最大取值255(根据卡类型)
            byte blocknum = 12;               //指定读取块数,最大取值12
            byte[] myuid = new byte[8];       //8字节卡序列号缓冲
            byte[] mydatabuf = new byte[255]; //读卡数据缓冲共255个字节

            status = (int) (CLibrary.sdtapi.iso15693readex(myctrlword, afi, startblock, blocknum, myuid, mydatabuf) & 0xff);
            System.out.print("\n2-轻松读15693卡\n");
            if (status == 0) {
                CLibrary.sdtapi.pcdbeep(38);
                String databuf = "";
                for (int i = 1; i < mydatabuf[0]+1; i++) {
                    String bytestr = "00" + Integer.toHexString(mydatabuf[i] & 0xff);
                    databuf = databuf + bytestr.substring(bytestr.length() - 2, bytestr.length())+" ";
                }
                System.out.print("卡内数据:" + databuf +"\n");
            } 
   

3、 RFID 写入

注:写数据到在感应区的卡的从某块开始的N块中,同时返回卡号

 		    int status;                            //存放返回值
            byte myctrlword = 0;                   //读写控制字,一般取0 就行了
            byte afi = 0;                          //卡片应用标识
            byte startblock = 0;                   //指定写起始块地址,最大取值255(根据卡类型)
            byte blocknum = 12;                    //指定写块数,最大取值12
            byte writeBufLen=(byte)(blocknum*4);   //写入数据长度,每块4个字节
            byte[] myuid = new byte[8];            //8字节卡序列号缓冲
            byte[] WriteBuf = new byte[48];        //写卡缓冲,

            WriteBuf[0]=writeBufLen;                //写卡缓冲的第一个字节是本次要写入的长度
            //写中文或字母数字等字符信息,将要写入的字符转ASCII码写入
            String WriteStr = "1949年,伟大的中国人民从此站立起来!"; //将要写入的文字生成字节数组
            byte[] strbuf = WriteStr.getBytes();
            for (int i = 0; i < 47; i++) {           //指定写卡数据,最长47个字节,其中首字节是写信息的长度
                WriteBuf[i+1] = strbuf[i];
            }

            status = (int) (CLibrary.sdtapi.iso15693writeex(myctrlword, afi, startblock, blocknum, myuid, WriteBuf) & 0xff);
            System.out.print("\n3-轻松写15693卡\n");
            if (status == 0) {
                CLibrary.sdtapi.pcdbeep(38);
                String serialnumber = "";
                for (int i = 0; i < 8; i++) {
                    String bytestr = "00" + Integer.toHexString(myuid[i] & 0xff);
                    serialnumber = serialnumber + bytestr.substring(bytestr.length() - 2, bytestr.length());
                }
                System.out.print("写卡成功,卡序列号:" + serialnumber+"\n");
            } 

说明:由于个人对硬件底层不是很了解,因此文章会存在许多不足之处,只是停留在对硬件的操作层面上。如果你需要了解更多,请自行补充。

动态库和jar包分享:

链接:https://pan.baidu.com/s/1fHkTTDFdKOHPrLnSw5AnCg 提取码:rfyi

参考文章:https://baijiahao.baidu.com/s?id=1659632229606313959&wfr=spider&for=pc

end

评论