I am trying to change the buzzer duration on the ACR1252U.
Link to API: http://www.acs.com.hk/download-manual/6402/API-ACR1252U-1.09.pdf
According to the API documentation I need the 'E0000028010A' command to change the buzzer status, whereby '0A' marks the duration as 0A*10ms (Page 44).
Following Java code is used:
public static void main(String[] args) {
try {
byte[] send = new byte[6];
send[0] = (byte) 0xE0; // Commandclass
send[1] = (byte) 0x00; // Protocoll
send[2] = (byte) 0x00; // Param 1
send[3] = (byte) 0x28; // Param 2: Buzzerstatus
send[4] = (byte) 0x01; // Change Flag
send[5] = (byte) 0x0A; // Duration: 0A*10ms => 100ms
Card card = getCard("DIRECT"); // Works!
CardChannel channel = card.getBasicChannel(); // Works!
CommandAPDU command = new CommandAPDU(send); // Works!
channel.transmit(command); // EXCEPTION!
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static Card getCard(String target) throws Exception {
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
for (CardTerminal t : terminals) {
if (t.getName().equals("ACS ACR1252 Dual Reader PICC 0")) {
Card card = t.connect(target);
return card;
}
}
throw new Exception();
}
But this results in the following stacktrace indicating the "unkown error 0x16":
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x16
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:219)
at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
at readerconfig.TagConfig.main(TagConfig.java:24)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x16
at sun.security.smartcardio.PCSC.SCardTransmit(Native Method)
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:188)
... 2 more
I've spent hours on searching for anything in this direction however I couldn't find anything. I have even tried another device, which still generated this error.
Either I have completely gone blind or something is not set up correctly with my computer. All I can say is, that I have already successfully written and read from NFC tags using this reader. But I just can't change the config of the reader itself.
EDIT:
I've also found this alternative way to send the command:
byte[] send = new byte[5];
send[0] = (byte) 0xE0;
send[1] = (byte) 0x0;
send[2] = (byte) 0x0;
send[3] = (byte) 0x18; // Tries to read firmware version
send[4] = (byte) 0x0;
Card card = CardUtils.getCard("DIRECT"); // Works!
card.transmitControlCommand(3500, send);
But this results in the "unknown error 0x1":
javax.smartcardio.CardException: transmitControlCommand() failed
at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:236)
at readerconfig.ReaderConfig.main(ReaderConfig.java:28)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x1
at sun.security.smartcardio.PCSC.SCardControl(Native Method)
at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:232)
... 1 more
Try to use
instead of transmit. According to section 5.8 (page 41 of the pdf you linked to) controlcode is 3500, although it is unclear to me, if that is a hex or an int, so compare to SCARD_CTL_CODE, if you are able to. At least, I interpret the documentation this.
Usually you use transmitControlCommand to talk to the reader and transmit to talk to the card.
Fixed typo in ControlCode. Kudos to Torhan Bartel for telling me.
There are two ways to interact with this reader over the Java Smartcard IO API:
The first is to open a regular APDU transmission channel (from a PC/SC point of view this maps to T=0 or T=1 protocol). You can do this using
However, this will require the reader to report the presence of a card. Otherwise you can't open a connection that way.
You can then transmit APDU commands to the card (on the basic channel or a logical channel) and you can send special commands to the reader on the basic channel. These special commands have their class byte set to 0xFF to indicate that the command is intended to be interpreted by the reader (instead of being forwarded to the card). So this is not applicable for the "peripherals control" commands that start with 0xE0.
Those "peripherals control" commands have to be sent to the reader using control commands with the control code
SCARD_CTL_CODE(3500)
. As with opening a connection to the card, you can usegetCard("*")
if there is a card present on the reader. However, if you want to be able to send those commands to the reader even if there is no card present, you have to open a connection in "direct" mode:You can then send control commands using the method
card.transmitControlCommand()
. This method takes the control code as the first argument and the command (as byte array) as the second argument. Exchanging commands on the basic channel or any logical channel usingchannel.transmit()
will usually not work in "direct" mode (hence the error code 0x16).The control code is calculated as
Note the difference between Windows and other platforms.
For instance, to send the buzzer control command, you would use
Finally, be aware that sending IOCTL control codes over PC/SC requires special driver support. Specifically, the standard CCID driver provided by Microsoft does not support sending escape commands by default (see USB CCID Class Driver Details). This driver supports escape commands only after enabling them through the registry value "EscapeCommandEnable". The error
0x1
that you showed in your question is a typical result of this missing support for escape commands.To reliably support all features of the reader (including escape commands) you need to use the "PC/SC Drivers" package provided by ACS on their website.