element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Internet of Things
  • Technologies
  • More
Internet of Things
Blog Digital Logic µFR NFC Card Reader - part 2: first C++ program
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Internet of Things to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 15 Dec 2020 2:33 PM Date Created
  • Views 2053 views
  • Likes 3 likes
  • Comments 0 comments
  • raspberry
  • eclipse
  • nfc
Related
Recommended

Digital Logic µFR NFC Card Reader - part 2: first C++ program

Jan Cumps
Jan Cumps
15 Dec 2020

In this article I make a c++ application for the D-Logic card reader and a set of smart cards.

It's again a simple console program that checks the type of card is on top of the reader, and get the card ID. The purpose is to show that you can use it in a c++, with objects.

image

I have a Digital Logic µFR Nano reader.

I'm using D-Logic's API and library, on a Linux device (say: BB, Raspberry Pi, ...).

My development environment is Eclipse on a Windows 10 laptop, with ARM-Linux cross compile toolchain.

 

Simple c++ Framework

 

This is a simple and incomplete framework. A proof of concept. And an excuse to refresh my c++ knowledge.

I defined a few goals for the functionality:

  • must be able to contact NFC reader
  • must be able to detect card
  • must be able to retrieve card ID

 

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

... and a few for the c++ specifics:

  • reader class represents the device, can initialise and clean upitself,
  • reader can detect if a card is in range
  • reader can return a card object if a card is available
  • card class represents a card
  • created and release by the reader
  • can stream its id
  • exception handling
  • class enumeration types
  • use hip c++ concepts: stl container, iterator, streaming, namespace

 

I'm listing the class declarations and some selected implementation code below. The full code is attached as a Eclipse project.

My choices are debatable. It does make for a neat main flow though:

 

int main() {
  Reader *reader = NULL;
  try {
    reader = UfrFactory::getReader();
    bool hasCard = reader->hasCard();

    if (hasCard) {  // there's a card on the reader
      Card *crd = reader->getCard();
      std::cout << "card id: " << *crd << std::endl;
      reader->releaseCard(crd);
      crd = NULL;

    } else { // there's no card on the reader
      std::cout << "no card" << std::endl;
    }

  } catch (const ReaderException &e) {
    std::cerr << e.what() << ": 0x" << std::hex << e.getError() << std::dec << std::endl;
  }

  UfrFactory::releaseReader(reader);
  reader = NULL;
  return 0;
}

 

I ask the framework for a card reader. It checks if there is a card in reach.

If yes, it prints the card ID.

image

 

The Reader family

 

The Reader is an abstract class. The interface for different types of hardware.

The UfrReader later on is a concrete imlementation.

 

class Reader {
public:
Reader();
virtual ~Reader();

virtual void readerOpen() = 0;
virtual void readerClose() = 0;

virtual bool hasCard() = 0;
virtual Card * getCard() = 0;
virtual void releaseCard(Card *card);
};

 

class ReaderException : public std::exception {
public:
  ReaderException(UfrFactory::Status error) : error{error} {
}
  virtual const char*  what() const noexcept;
  UfrFactory::Status getError() const {
  return this->error;
    }
private:
    UfrFactory::Status error;
};

 

class UfrReader : public Reader {
public:
  UfrReader();
  virtual ~UfrReader();

  virtual void readerOpen() override;
  virtual void readerClose() override;

  virtual bool hasCard() override;
  virtual Card* getCard() override;
};

 

The Card

 

A simple interface. Some highlights:

  • a Card holds a reference to the Reader that created it. It isn't used, I wanted to train having hard constant references in a class.
    It can be useful later, if there is support for more than one reader.
  • the class can stream itself. When you send it to an output stream, it shows its key formatted: all key bytes in hex with colons in between (e.g.: 64:20:ed:4a).

 

class Card {
public:
  Card(const Reader *reader, const uint8_t sac, const std::array<uint8_t, 10> id, const uint8_t size);
  virtual ~Card();
  virtual const std::array<uint8_t, 10>& getId() {
    return this->id;
}

virtual const uint8_t getSize() {
  return this->size;
}

friend std::ostream& operator<< (std::ostream &out, const Card &card);

private:
  const Reader *reader;
  const uint8_t sac;
  const std::array<uint8_t, 10> id;
  const uint8_t size;
};

 

The Utility family

 

One utility only. It knows the error codes, and how to convert from D-Logic's C enumerator to the class enumerator type.

It can create and release reader objects.

 

class UfrFactory {
public:
  enum Status {
  UFR_OK = 0x00,
  UFR_COMMUNICATION_ERROR = 0x01,
  UFR_CHKSUM_ERROR = 0x02,
  UFR_READING_ERROR = 0x03,
  UFR_WRITING_ERROR = 0x04,
  UFR_BUFFER_OVERFLOW = 0x05,
  UFR_MAX_ADDRESS_EXCEEDED = 0x06,
  UFR_MAX_KEY_INDEX_EXCEEDED = 0x07,
  UFR_NO_CARD = 0x08,
// ... a number of codes left out for readability
  MAX_UFR_STATUS = 0x7FFFFFFF
};

  UfrFactory();
  virtual ~UfrFactory();

  static inline UFR_STATUS retvalConvert(Status status) {
    return (UFR_STATUS)status;
  }
  static inline  Status retvalConvert( UFR_STATUS status) {
    return (Status)status;
  }

  static Reader * getReader();
  static void releaseReader(Reader *reader);
};

 

Implementation

 

Getting the reader and give it back when finished:

 

int main() {
  Reader *reader = NULL;
  try {
    reader = UfrFactory::getReader();

    // do stuff

  } catch (const ReaderException &e) {
    std::cerr << e.what() << ": 0x" << std::hex << e.getError() << std::dec << std::endl;
  }
  UfrFactory::releaseReader(reader);
  reader = NULL;

  return 0;
}

 

The factory doesn't give a Reader. That's an abstract class. Instead, it will return a specific implementation.

In my code, this is always the one for the µFR Nano reader. But it's possible to check what readers are attached and create the right type.

Here's the factory method. It delivers an opened reader.

 

Reader* UfrFactory::getReader() {
  Reader *reader = new UfrReader();

  try {
    reader->readerOpen();
  } catch (const ReaderException &e) {
    std::cout << e.what() << ": 0x" << std::hex << e.getError() << std::dec << std::endl;
  }

  return reader;
}

 

I've also provided a release function. I prefer - as a first decision point, there are several valid options - to let the creator dispose of objects. Open for debate.

The reader gets closed and destructed.

 

void UfrFactory::releaseReader(Reader *reader) {
  if (reader) {
    try {
      reader->readerClose();
    } catch (const ReaderException &e) {
      std::cout << e.what() << ": 0x" << std::hex << e.getError() << std::dec << std::endl;
    }
    delete reader;
  }
}

 

The Reader's readerOpen() and readerClose() methods have no surprises:

 

void UfrReader::readerOpen() {
  UfrFactory::Status status = UfrFactory::retvalConvert(ReaderOpen());
  if (status != UfrFactory::UFR_OK) {
    throw ReaderException(status);
  }
}

void UfrReader::readerClose() {
  UfrFactory::Status status = UfrFactory::retvalConvert(ReaderClose());
  if (status != UfrFactory::UFR_OK) {
    throw ReaderException(status);
  }
}

 

 

Checking for cards, retrieve a Card object, give back when finished:

 

  bool hasCard = reader->hasCard();
  if (hasCard) {  // there's a card on the reader
    Card *crd = reader->getCard();

     // do stuff

    reader->releaseCard(crd);
    crd = NULL;

  } else { // there's no card on the reader
    std::cout << "no card" << std::endl;
  }

 

The getCard function is made to quickly check if there's a card.

Unless other card related functions, that throw an exception if there's no card in reach, this one will return false.

That's a design decision. Throwing an exception if you expect a card, but there is none, is ok.

But for this function, no physical card on the reader is a valid case, not exceptional. So it should not throw an exception, but should give us the info.

 

bool UfrReader::hasCard() {
  uint8_t lpucSak = 0;
  uint8_t  aucUid[10];
  uint8_t lpucUidSize = 0;
   return (UfrFactory::retvalConvert(GetCardIdEx(&lpucSak, aucUid, &lpucUidSize)) == UfrFactory::UFR_OK);
}

 

The three parameters aren't of interest at this time. We're just looking for the return value of the uFR libary call.

 

The getCard() method creates a new Card, tells it what its ID is, and gives it a reference to itself.

This one throws an exception if there's no card. Because it can't fulfil its contractual duties.

 

Card* UfrReader::getCard(){
  uint8_t lpucSak = 0;
  uint8_t  aucUid[10];
  uint8_t lpucUidSize = 0;
  std::array<uint8_t, 10> id;

  UfrFactory::Status status = UfrFactory::retvalConvert(GetCardIdEx(&lpucSak, aucUid, &lpucUidSize));
  if (status != UfrFactory::UFR_OK) {
    throw ReaderException(status);
  }

  std::copy(std::begin(aucUid), std::end(aucUid), id.begin());
  return new Card(this, lpucSak, id, lpucUidSize);
}

 

It uses the uFR api to get the base card info and, when creating a new Card object, passes these to the constructor.

I'm using an initialisation list here to prime all data members. They are card family, card ID and the length of that ID.

I have to do that. I made them constant (because I want them to be immutable as soon as the object is created).

Constant members have to be initialised when construction, with an initialisation list.

 

Card::Card(const Reader *reader, const uint8_t sac, const std::array<uint8_t, 10> id, const uint8_t size) :
  reader { reader }, sac { sac }, id { id }, size { size } {
}

 

No code will be able to alter these values once constructed. That 'll take care that we can use them as check points when talking to the card later.

 

The releaseCard() function is simple. It's implemented in the Reader base class, not in UfrReader, because it just discards the object. Open for debate.

 

void Reader::releaseCard(Card * card) {
  delete card;
}

 

Doing something with the Card:

 

The ultimate goal is to make the Card object so smart that it can negociate encryption, hash code generation and other nice functionality with the smart card.

Later image. For now, I gave it the skill to stream itself to an output stream. c++ style.

 

Here, on line 2,  you see that we can stream it to the console of our OS (in this case Linux):

 

  Card *crd = reader->getCard();
  std::cout << "card id: " << *crd << std::endl;
  reader->releaseCard(crd);

 

You have to learn your object how to do that streaming. How the create the desired output and formatting.

byte : byte : .... : byte

image

 

class Card {
public:
  // ... stuff
  friend std::ostream& operator<< (std::ostream &out, const Card &card);
private:
  // ... more stuff

 

I created a streamer. It's made friend, so that later on we can access the class data members directly.

That's not an encapsulation breach, because it's made explicit.

 

std::ostream& operator<<(std::ostream &out, const Card &card) {
  out << std::hex;
  std::array<const uint8_t, 10>::iterator i;

  for (i = card.id.begin(); i != card.id.begin() + card.size; ++i) {
    if (i != card.id.begin()) {
      out << ":";
    }
    out << (unsigned) (*i);
  }

  out << std::dec;
   return out;
}

 

Some c++ concepts here. First of all the streaming itself.

Then, I use an iterator to feed the for loop.

I've gone for a reasonable traditional loop, because I don't want to iterate over the whole array. Only on those elements that are within the ID size.

I'm open for debate here too.The STL has some other concepts and iterator types.

 

That's that. In summary:

  • a factory that can set up a Reader object
  • a Reader can detect if there is a card, and set up a Card object
  • a Card can stream its ID formatted.

Humble setup, but ready for extension. The Exlipse project is attached.

 

 

Related Blog
Blockchain - Talk Directly to the Infineon 2Go Smart Cards API
NFC Card Reader Protocol of Digital Logic µFR - part 0: intent
µFR NFC Card Reader - part 1: first C program for Linux (RPi, BB, ...)
µFR NFC Card Reader - part 2: first C++ program
µFR NFC Card Reader - part 3: C++ Class to handle ISO14443 / APDU cards
µFR NFC Card Reader - part 4: first meaningful ISO14443 / APDU conversation
µFR NFC Card Reader - part 5: refactor, look back at initial design
Attachments:
digitallogic_rfc_cpp_20201215.zip
  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube