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!

2
  • Are you specifically looking to do this using python?
    – zenzelezz
    Commented Jul 31, 2015 at 18:30
  • @zenzelezz No, not specifically in Python. I'm using Python just because I'm new to the Raspberry Pi and a lot of sample code seems to be done in Python. I would be ok if I or someone else finds a solution in another language.
    – zundi
    Commented Jul 31, 2015 at 18:57

1 Answer 1

3
+50

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++.

4
  • There are also python bindings for PCSC. pypi.python.org/pypi/pyscard
    – John Tyree
    Commented Aug 4, 2015 at 16:43
  • 1
    As a note, in my case I found that the device wasn't listed in Info.plist (in /usr/lib/pcsc/drivers/ifd-acsccid.bundle/Contents/) so I went ahead and added my info in the ifdVendorID (0x072F), ifdProductID (0x223B), and ifdFriendlyName (ACS ACR1252U Dual PICC-SAM Reader) arrays, and ran pcscd -fd. Only after this step did pcsc_scan actually detect my device and everything else started working after.
    – zundi
    Commented Aug 6, 2015 at 20:29
  • Thank you for that tip; we are most likely going to be switching to the 1252U at work, so that's good to know :)
    – zenzelezz
    Commented Aug 6, 2015 at 20:46
  • @zundi's comment should really be part of the answer :o) Commented May 23, 2016 at 13:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.