如何在Android中使用ACR35 NFC Reader

时间:2015-02-20 08:18:15

标签: android nfc

我是使用ACR或NFC阅读器开发的新手,特别是对于Android。最近我需要使用ACR35,我已经获得了来自this acs official website的SDK和示例。它的工作原理很好。 现在我需要创建一个活动,随时可以检查nfc卡是否被点击。但问题是我不知道如何检测nfc卡何时被点击,我不知道接下来该做什么,而且我无法从示例中找到出路,因为它检测到nfc卡,当我触摸'传输'按钮,它不会自动执行。 请给我一些示例代码。 谢谢你的回答。

2 个答案:

答案 0 :(得分:4)

有点晚了,但它可能对某人有所帮助。 我在git hub上找到了以下项目。它显示了如何从acr35读取标签ID here

我已经使用acr35一段时间来阅读标签ID。根据我的经验,Android上有一个有缺陷的设备。我测试了大约10台设备,它只在3 ...

上工作

我每秒都读它。它返回最后一张卡的结果,即使它不再存在。因此;每次成功读取都需要重置两次,大约需要6秒钟才能让设备再次处于读取状态...同时要非常小心多线程。

我的实现基于提到的项目 - 添加了简单的锁定,以便在成功读取+完整设备重置后停止查询卡片,filternig同样的uuid,在之前读取后很短的时间内读取:

ACR3x类

import com.acs.audiojack.AudioJackReader;

import android.media.AudioManager;
import sk.tido.util.ByteHex;

import java.util.Date;

/**
 * This class allows control of the ACR35 reader sleep state and PICC commands
 */
public class Acr3x {

    private Acr3xTransmitter transmitter;
    private AudioManager mAudioManager;
    private AudioJackReader mReader;

    private boolean firstReset = true;  /** Is this the first reset of the reader? */

    /** APDU command for reading a card's UID */
    private final byte[] apdu = { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
    /** Timeout for APDU response (in <b>seconds</b>) */
    private final int timeout = 1;
    private int acr3xCardType = AudioJackReader.PICC_CARD_TYPE_ISO14443_TYPE_A
            | AudioJackReader.PICC_CARD_TYPE_ISO14443_TYPE_B
            | AudioJackReader.PICC_CARD_TYPE_FELICA_212KBPS
            | AudioJackReader.PICC_CARD_TYPE_FELICA_424KBPS
            | AudioJackReader.PICC_CARD_TYPE_AUTO_RATS;

    private int acr3xStartAudioLevel = 0;

    private Object locking = new Object();

    private String lastUuid = "";
    private Date lastUuidDate = new Date();


    public Acr3x(AudioManager mAudioManager){
        this.mAudioManager = mAudioManager;
    }

    public void start(final Acr3xNotifListener listener){
        Runnable r = new Runnable(){

            @Override
            public void run() {
                if(mReader == null){
                    mReader = new AudioJackReader(mAudioManager);
                }
                System.out.println("ACR35 reader start");

                acr3xStartAudioLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                System.out.println("acr3x start audio stream level: " + acr3xStartAudioLevel);
                mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
                        mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
                System.out.println("acr3x set audio stream level: " + mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));

                mReader.start();
                mReader.setSleepTimeout(30);
                mReader.setOnFirmwareVersionAvailableListener(new AudioJackReader.OnFirmwareVersionAvailableListener() {
                    @Override
                    public void onFirmwareVersionAvailable(AudioJackReader reader,
                        String firmwareVersion) {
                        System.out.println("acr3x firmware version: " + firmwareVersion);
                        if(listener != null){
                            listener.onFirmwareVersionAvailable(firmwareVersion);
                        }
                        Acr3x.this.read(listener);

                    }
                });
                mReader.reset(new AudioJackReader.OnResetCompleteListener(){

                    @Override
                    public void onResetComplete(AudioJackReader arg0) {
                        mReader.getFirmwareVersion();

                    }

                });
            }
        };

        Thread t = new Thread(r, "Acr3xInitThread");
        t.start();

    }


    /**
     * Sets the ACR35 reader to continuously poll for the presence of a card. If a card is found,
     * the UID will be returned to the Apache Cordova application
     *
     * @param callbackContext: the callback context provided by Cordova
     * @param cardType: the integer representing card type
     */
    public void read(final Acr3xNotifListener callbackContext){
        System.out.println("acr3x setting up for reading...");
        firstReset = true;

        /* Set the PICC response APDU callback */
        mReader.setOnPiccResponseApduAvailableListener
                (new AudioJackReader.OnPiccResponseApduAvailableListener() {
                    @Override
                    public void onPiccResponseApduAvailable(AudioJackReader reader,
                                                            byte[] responseApdu) {
                        /* Update the connection status of the transmitter */
                        transmitter.updateStatus(true);

                        /* Print out the UID */
                        String uuid = ByteHex.bytesToHex(responseApdu);

                        if(uuid.equalsIgnoreCase("0x9000")){
                            return;
                        }

                        if(uuid.endsWith("9000")){
                            uuid = uuid.substring(0, uuid.length() - 4);
                        }

                        if(uuid.equals(lastUuid)){ // na odfiltrovanie opakujucich sa uuid z citacky z predchadzajuceho citania
                            if(new Date().getTime() - lastUuidDate.getTime() < 3000){
                                return;
                            }
                        }

                        lastUuid = uuid;
                        lastUuidDate = new Date();

                        synchronized(locking){

                            System.out.println("acr3x uuid: " + uuid);

                            if(callbackContext != null){
                                callbackContext.onUUIDAavailable(uuid);
                            }
                            System.out.println("acr3x restarting reader");
                            transmitter.kill();
                            try {
                                locking.wait(2000);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }

                        read(callbackContext);



                    }
                });

        /* Set the reset complete callback */
        mReader.setOnResetCompleteListener(new AudioJackReader.OnResetCompleteListener() {
            @Override
            public void onResetComplete(AudioJackReader reader) {
                System.out.println("acr3x reset complete");

                /* If this is the first reset, the ACR35 reader must be turned off and back on again
                   to work reliably... */
                Thread t = null;
                if(firstReset){  //firstReset
                    t = new Thread(new Runnable() {
                        public void run() {
                            try{
                                /* Set the reader asleep */
                                mReader.sleep();
                                /* Wait one second */
                                Thread.sleep(500);
                                /* Reset the reader */
                                mReader.reset();

                                firstReset = false;
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                // TODO: add exception handling
                            }
                        }
                    });

                } else {
                    /* Create a new transmitter for the UID read command */
                    transmitter = new Acr3xTransmitter(mReader, mAudioManager, timeout,
                            apdu, acr3xCardType, locking);
                    t = new Thread(transmitter);
                }
                t.start();
            }
        });

        mReader.start();
        mReader.reset();
        System.out.println("acr3x setup complete");
    }

    public void stop(){
        if(transmitter != null){
            transmitter.kill();
        }

        System.out.println("acr3x restoring audio level: " + acr3xStartAudioLevel);
        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,acr3xStartAudioLevel, 0);
        System.out.println("acr3x set audio stream level: " + mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));

        if(mReader != null){
            mReader.stop();
        }
    }

}

发射器类

    import com.acs.audiojack.AudioJackReader;

import android.media.AudioManager;

/**
 * This class sets up an independent thread for card polling, and is linked to the
 * <code>setOnPiccResponseApduAvailableListener</code> callback function
 */

public class Acr3xTransmitter implements Runnable {

    private AudioJackReader mReader;
    private AudioManager mAudioManager;
    //private CallbackContext mContext;

    private boolean killMe = false;          /** Stop the polling thread? */
    private int itersWithoutResponse = 0;    /** The number of iterations that have passed with no
                                                 response from the reader */
    private boolean readerConnected = true;  /** Is the reader currently connected? */

    private int cardType;
    private int timeout;
    private byte[] apdu;
    private Object locking;

    /**
     * @param mReader: AudioJack reader service
     * @param mAudioManager: system audio service
     * @param mContext: context for plugin results
     * @param timeout: time in <b>seconds</b> to wait for commands to complete
     * @param apdu: byte array containing the command to be sent
     * @param cardType: the integer representing card type
     */
    public Acr3xTransmitter(AudioJackReader mReader, AudioManager mAudioManager,
                       int timeout, byte[] apdu, int cardType, Object locking){
        this.mReader = mReader;
        this.mAudioManager = mAudioManager;
        this.timeout = timeout;
        this.apdu = apdu;
        this.cardType = cardType;
        this.locking = locking;
    }


    /**
     * Stops the polling thread
     */
    public void kill(){
        killMe = true;
    }


    /**
     * Updates the connection status of the reader (links to APDU response callback)
     */
    public void updateStatus(boolean status){
        readerConnected = status;
    }

    /**
     * Sends the APDU command for reading a card UID every second
     */
    @Override
    public void run() {
        try {
            /* Wait one second for stability */
            Thread.sleep(1000);

            while (!killMe) {
                synchronized(locking){

                    if(killMe){
                        continue;
                    }
                    /* If the reader is not connected, increment no. of iterations without response */
                    if(!readerConnected){
                        itersWithoutResponse++;
                    }
                    /* Else, reset the number of iterations without a response */
                    else{
                        itersWithoutResponse = 0;
                    }
                    /* Reset the connection state */
                    readerConnected = false;

                    if(itersWithoutResponse == 4) {
                        /* Communicate to the Cordova application that the reader is disconnected */
                        System.out.println("acr3x disconnected");
                        /* Kill this thread */
                        kill();
                    } else if(!mAudioManager.isWiredHeadsetOn()) {
                        System.out.println("acr3x not connected");
                        /* Kill this thread */
                        kill();
                    } else{
                        System.out.println("acr3x reading...");
                        /* Power on the PICC */
                        mReader.piccPowerOn(timeout, cardType);
                        /* Transmit the APDU */
                        mReader.piccTransmit(timeout, apdu);


                    }
                }
                /* Repeat every second */
                Thread.sleep(1000);
            }
            /* Power off the PICC */
            mReader.piccPowerOff();
            /* Set the reader asleep */
            mReader.sleep();
            /* Stop the reader service */
            mReader.stop();

            synchronized(locking){
                locking.notifyAll();
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
            // TODO: add exception handling
        }
    }

}

监听器接口:

public interface Acr3xNotifListener {

public void onUUIDAavailable(String uuid);
public void onFirmwareVersionAvailable(String firmwareVersion);

}

答案 1 :(得分:0)

首先调用Reset命令激活设备。由于设备将入睡(默认情况下为4秒)*诀窍是保持它活着。

您可以通过每次上一次PowerOn timedOut重新启动新的PowerOn命令来实现这一点。像这样的东西

void powerOn () {
if (!mReader.piccPowerOn(mPiccTimeout, mPiccCardType)) {
   powerOn();
} else {
   askNfcForItsId();
}

在你读完nfc之后别忘了给powerOn打电话,否则它会在第一张nfc卡后睡着。

*重置超时可以设置为4到20秒