6

I'm using a ACS ACR1252U card reader (http://www.acs.com.hk/en/products/342/acr1252u-usb-nfc-reader-iii-nfc-forum-certified-reader/) connected via USB and to be honest I have no idea how to get it working. I've Googled a ton of stuff but no luck. The following is more or less what I've done:

I get the following using dmesg:

[ 7173.059710] usb 1-1.3: new full-speed USB device number 6 using dwc_otg
[ 7173.160500] usb 1-1.3: not running at top speed; connect to a high speed hub
[ 7173.163114] usb 1-1.3: New USB device found, idVendor=072f, idProduct=223b
[ 7173.163147] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 7173.163168] usb 1-1.3: Product: ACR1252 Dual Reader
[ 7173.163186] usb 1-1.3: Manufacturer: ACS

I used the following Python code to get more details about the devices:

#!/usr/bin/python

import sys
import usb.core

# find USB devices
dev = usb.core.find(find_all=True)


# loop through devices, printing vendor and product ids in decimal and hex
for cfg in dev:
    print cfg
    sys.stdout.write('Decimal VendorID=' + str(cfg.idVendor) + ' & ProductID=' + str(cfg.idProduct) + '\n')
    sys.stdout.write('Hexadecimal VendorID=' + hex(cfg.idVendor) + ' & ProductID=' + hex(cfg.idProduct) + '\n\n')

The output for this specific device is:

DEVICE ID 072f:223b on Bus 001 Address 004 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x072f
 idProduct              : 0x223b
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 ACS
 iProduct               :    0x2 ACR1252 Dual Reader
 iSerialNumber          :    0x0 
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 200 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0xb1 (177 bytes)
   bNumInterfaces       :    0x2
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0 
   bmAttributes         :   0x80 Bus Powered
   bMaxPower            :   0x64 (200 mA)
    INTERFACE 0: Smart Card ================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x3
     bInterfaceClass    :    0xb Smart Card
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x4 ACR1252 Dual Reader PICC
      ENDPOINT 0x1: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x84: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x84 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0xa
    INTERFACE 1: Smart Card ================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x1
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x3
     bInterfaceClass    :    0xb Smart Card
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x5 ACR1252 Dual Reader SAM
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x82: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x82 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x83: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x83 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0xa
Decimal VendorID=1839 & ProductID=8763
Hexadecimal VendorID=0x72f & ProductID=0x223b

I installed PyUSB and I'm able to detect the device using some of the sample code, but nothing like reading the NFC card when it comes into proximity (although there is a beeping sound, which makes me believe the USB device itself is working fine).

I also installed libacsccid1 package which says it supports this device https://github.com/acshk/acsccid, but I don't know exactly how to use this.

Besides that, I don't know what else to do. Any help will be greatly appreciated. The goal of my project is to control a GPIO output dependent on the value of the NFC tag detected.

--- Update ---

I've worked a bit more and although I still can't get it working, I have more data. Specifically I'm working off of this: http://www.digitalmihailo.com/usb-programming-with-python-on-linux-pyusb-version/

From the datasheets I can confirm it's a USB 2.0 full speed device:

enter image description here

For testing, I just want to make the buzzer sound. Here's the screenshot of the device's API doc for this functionality:

enter image description here

So, a simplified version of http://www.digitalmihailo.com/usb-programming-with-python-on-linux-pyusb-version/ for my device would be:

#!/usr/bin/python

import os
import sys
import time

import usb.core
import usb.util


# According to what I've read, full speed USB is 64 byte packet size.
packet_len = 64

# Packing a request.
# Packets are 64 bytes long, most of the commands are 4 bytes long. So up to 18
# can be batched into a packet. For example command with bytes [0x94, 0x0, 0x0, 0x0] is getting firmware id
def pack_request(*arguments):
    packet = [0x0] * packet_len
    i = 0
    for arg in arguments:
        packet[i] = arg
        i += 1
    #packet[0:4] = [0x94, 0x0, 0x0, 0x0] #get firmware id
    return ''.join([chr(c) for c in packet])


def main():
    #Updated for the ACS ACR1252U
    dev = usb.core.find(idVendor=0x72f, idProduct=0x223b)

    # was it found?
    if dev is None:
        raise ValueError('Device not found')

    try:
        dev.detach_kernel_driver(0)
    except: # this usually mean that kernel driver has already been dettached
        pass

    # ACS ACR1252U only has 1 configuration
    dev.set_configuration()

    # Interface 0: Dual Reader PICC. This is what we want
    # Interface 1: Dual Reader SAM
    try:
        dev.set_interface_altsetting(0)
    except usb.core.USBError:
        print 'Error setting interface!'
        pass

    # According to the API, to sound the buzzer we must send:
    #
    # Class: 0xe0
    # INS: 0x00
    # P1: 0x00
    # P2: 0x28
    # Lc: 0x01
    # DataIn (duration): 0xff (for 255 * 10ms = 2.55 seconds)
    raw = pack_request(0xe0, 0x00, 0x00, 0x28, 0x01, 0xff)

    #send the packet
    # Params are (endpoint, data, timeout)
    #
    # According to the script output:
    # ENDPOINT 0x01: Bulk OUT
    dev.write(endpoint=0x01, data=raw)
    #done


if __name__ == '__main__':
  main()

When I run this nothing happens... no buzzer, no error message. What could be wrong here?

Thanks!

zundi
  • 735
  • 2
  • 6
  • 10

1 Answers1

3

Okay, I had a go at this using my ACR1251U-A1, which is pretty similar to your model, and I got it working. First a short explanation, as far as I could understand the problem: the data that you were trying to send is not a complete, valid USB message; it is only the payload to be delivered to the reader. pyusb, libusb etc. seem to expect you to be fluent in USB.

Instead, you should be using a smartcard API, like PC/SC, which takes care of the intricacies of USB for you, such as PCSC lite through C/C++, pyscard in Python, or other APIs/bindings in other languages.

Edited: based on the link provided by John Tyree's comment, I now include a direct translation of the C++ code into Python. I'm sure true pythoners will hiss at my attempt, but consider it a proof of concept.

The code below is only an example limited to what your own test was doing; it is likely to have plenty of room for improvements. For example, it does not listen to when cards are presented.

Python version:

#!/usr/bin/python

from smartcard.scard import *

##

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_SYSTEM)

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardEstablishContext(): ' + SCardGetErrorMessage(hresult)

##

hrest, readers = SCardListReaders(hcontext, [])

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardListReaders(): ' + SCardGetErrorMessage(hresult)

##

hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, readers[0], SCARD_SHARE_DIRECT, 0)

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardConnect(): ' + SCardGetErrorMessage(hresult)

##

hresult, response = SCardControl(hcard, SCARD_CTL_CODE(3500), [0xE0, 0x0, 0x0, 0x28, 0x1, 0xFF])

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardControl(): ' + SCardGetErrorMessage(hresult)

print ''.join('{:02X} '.format(x) for x in response)

##

hresult = SCardDisconnect(hcard, SCARD_LEAVE_CARD)

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardDisconnect(): ' + SCardGetErrorMessage(hresult)

##

hresult = SCardReleaseContext(hcontext)

if hresult != SCARD_S_SUCCESS:
  raise error, 'SCardReaseContext(): ' + SCardGetErrorMessage(hresult)

C++ version:

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <cstdint>
#include <cstring>
#include <winscard.h>
#include <reader.h>

int main(int argc, char* argv[]) {
    SCARDCONTEXT context = 0;
    LONG ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, nullptr, nullptr, &context);

    if (ret != SCARD_S_SUCCESS) {
        std::cout << "SCardEstablishContext: " << pcsc_stringify_error(ret) << std::endl;
    } else {
        LPTSTR allReaderNames = nullptr;
        DWORD readerCount = SCARD_AUTOALLOCATE;

        ret = SCardListReaders(context, nullptr, reinterpret_cast<LPTSTR>(&allReaderNames), &readerCount);

        if (ret != SCARD_S_SUCCESS) {
            std::cout << "SCardListReaders: " << pcsc_stringify_error(ret) << std::endl;
        } else {
            std::string readerName{ allReaderNames };
            DWORD activeProtocol = 0;
            SCARDHANDLE card = 0;

            ret = SCardConnect(context, readerName.c_str(), SCARD_SHARE_DIRECT, 0, &card, &activeProtocol);

            if (ret != SCARD_S_SUCCESS) {
                std::cout << "SCardConnect: " << pcsc_stringify_error(ret) << std::endl;
            } else {
                std::vector<std::uint8_t> outputBuffer{ 0xE0, 0x0, 0x0, 0x28, 0x1, 0xFF };
                std::vector<std::uint8_t> inputBuffer(64, 0);
                DWORD bytesReturned = 0;

                ret = SCardControl(card, SCARD_CTL_CODE(3500), outputBuffer.data(), outputBuffer.size(), inputBuffer.data(), inputBuffer.size(), &bytesReturned);

                if (ret != SCARD_S_SUCCESS) {
                    std::cout << "SCardControl: " << pcsc_stringify_error(ret) << std::endl;
                } else {
                    std::cout << "Response: " << std::hex << std::setfill('0');
                    for (std::size_t i = 0; i < bytesReturned; ++i) {
                        std::cout << std::setw(2) << static_cast<std::uint32_t>(inputBuffer[i]) << " ";
                    }
                    std::cout << std::dec << std::endl;

                    SCardDisconnect(card, SCARD_LEAVE_CARD);
                }
            }

            // Release the memory that SCardListReaders allocated for us
            SCardFreeMemory(context, allReaderNames);
        }

        ret = SCardReleaseContext(context);

        if (ret != SCARD_S_SUCCESS) {
            std::cout << "SCardReleaseContext: " << pcsc_stringify_error(ret) << std::endl;
        }
    }

    return 0;
}

I compiled this using the command line g++-4.9 -std=c++11 -Wall -lpcsclite -I /usr/include/PCSC -o pcsctest. I believe it can also be done using an older version of g++.

zenzelezz
  • 196
  • 5