Home | History | Annotate | Download | only in sm130
      1 /*
      2  * Author: Jon Trulson <jtrulson (at) ics.com>
      3  * Copyright (c) 2015 Intel Corporation.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining
      6  * a copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sublicense, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 #include <stdexcept>
     26 
     27 #include "sm130.h"
     28 
     29 using namespace upm;
     30 using namespace std;
     31 
     32 // Uncomment the below to see packaet data sent and received from the SM130
     33 // #define SM130_DEBUG
     34 
     35 static const int defaultDelay = 1000; // ms for read
     36 
     37 static const int maxLen = 64; // max number of bytes to read
     38 
     39 SM130::SM130(int uart, int reset) :
     40   m_uart(uart), m_gpioReset(reset)
     41 {
     42   m_tagType = TAG_NONE;
     43   m_uidLen = 0;
     44   m_uid.clear();
     45   clearError();
     46   initClock();
     47 
     48   m_gpioReset.dir(mraa::DIR_OUT);
     49   m_gpioReset.write(0);
     50 }
     51 
     52 SM130::~SM130()
     53 {
     54 }
     55 
     56 mraa::Result SM130::setBaudRate(int baud)
     57 {
     58   m_baud = baud;
     59   return m_uart.setBaudRate(baud);
     60 }
     61 
     62 string SM130::sendCommand(CMD_T cmd, string data)
     63 {
     64   uint8_t cksum = 0;
     65   string command;
     66 
     67   // for uart, we need to add the sync bytes, 0xff, 0x00
     68   command.push_back(0xff);
     69   command.push_back(0x00);
     70 
     71   // compute the length - command + data
     72   uint8_t len = 1; // command
     73   if (!data.empty())
     74     len += data.size();
     75 
     76   command.push_back(len);
     77 
     78   cksum += len;
     79 
     80   // now the command
     81   command.push_back(cmd);
     82   cksum += cmd;
     83 
     84   // now the data if any
     85   if (!data.empty())
     86     {
     87       for (int i=0; i<data.size(); i++)
     88         {
     89           command.push_back(data[i]);
     90           cksum += (uint8_t)data[i];
     91         }
     92     }
     93 
     94   // now add the checksum
     95   command.push_back(cksum);
     96 
     97 #ifdef SM130_DEBUG
     98   cerr << "CMD: " << string2HexString(command) << endl;
     99 #endif // SM130_DEBUG
    100 
    101   // send it
    102   m_uart.writeStr(command);
    103 
    104   // if the command is SET_BAUD, then switch to the new baudrate here
    105   // before attempting to read the response (and hope it worked).
    106   if (cmd == CMD_SET_BAUD)
    107     {
    108       usleep(100000); // 100ms
    109       setBaudRate(m_baud);
    110     }
    111 
    112   // now wait for a response
    113   if (!m_uart.dataAvailable(defaultDelay))
    114     {
    115       cerr << __FUNCTION__ << ": timeout waiting for response" << endl;
    116       return "";
    117     }
    118 
    119   string resp = m_uart.readStr(maxLen);
    120 
    121 #ifdef SM130_DEBUG
    122   cerr << "RSP: " << string2HexString(resp) << endl;
    123 #endif // SM130_DEBUG
    124 
    125   if (!((uint8_t)resp[0] == 0xff && (uint8_t)resp[1] == 0x00))
    126     {
    127       cerr << __FUNCTION__ << ": invalid packet header" << endl;
    128       return "";
    129     }
    130 
    131   // check size - 2 header bytes + len + cksum.
    132   if (resp.size() != ((uint8_t)resp[2] + 2 + 1 + 1))
    133     {
    134       cerr << __FUNCTION__ << ": invalid packet length, expected "
    135            << int((uint8_t)resp[2] + 2 + 1 + 1)
    136            << ", got " << resp.size() << endl;
    137       return "";
    138     }
    139 
    140   // verify the cksum
    141   cksum = 0;
    142   for (int i=2; i<(resp.size() - 1); i++)
    143     cksum += (uint8_t)resp[i];
    144 
    145   if (cksum != (uint8_t)resp[resp.size() - 1])
    146     {
    147       cerr << __FUNCTION__ << ": invalid checksum, expected "
    148            << int(cksum) << ", got " << (uint8_t)resp[resp.size()-1] << endl;
    149       return "";
    150     }
    151 
    152   // we could also verify that the command code returned was for the
    153   // command submitted...
    154 
    155   // now, remove the 2 header bytes and the checksum, leave the length
    156   // and command.
    157   resp.erase(resp.size() - 1, 1); // cksum
    158   resp.erase(0, 2);               // header bytes
    159 
    160   // return the rest
    161   return resp;
    162 }
    163 
    164 string SM130::getFirmwareVersion()
    165 {
    166   clearError();
    167 
    168   string resp = sendCommand(CMD_VERSION, "");
    169 
    170   if (resp.empty())
    171     {
    172       cerr << __FUNCTION__ << ": failed" << endl;
    173       return "";
    174     }
    175 
    176   // delete the len and cmd, return the rest
    177   resp.erase(0, 2);
    178   return resp;
    179 }
    180 
    181 bool SM130::reset()
    182 {
    183   clearError();
    184 
    185   string resp = sendCommand(CMD_RESET, "");
    186   if (resp.empty())
    187     {
    188       cerr << __FUNCTION__ << ": failed" << endl;
    189       return false;
    190     }
    191 
    192   return true;
    193 }
    194 
    195 void SM130::hardwareReset()
    196 {
    197   m_gpioReset.write(1);
    198   usleep(100000);
    199   m_gpioReset.write(0);
    200 }
    201 
    202 bool SM130::select()
    203 {
    204   clearError();
    205 
    206   m_tagType = TAG_NONE;
    207   m_uidLen = 0;
    208   m_uid.clear();
    209 
    210   string resp = sendCommand(CMD_SELECT_TAG, "");
    211 
    212   if (resp.empty())
    213     {
    214       cerr << __FUNCTION__ << ": failed" << endl;
    215       return false;
    216     }
    217 
    218   if ((uint8_t)resp[0] == 2)
    219     {
    220       // then we got an error of some sort, store the error code, str
    221       // and bail.
    222       m_lastErrorCode = resp[2];
    223 
    224       switch (m_lastErrorCode)
    225         {
    226         case 'N': m_lastErrorString = "No tag present";
    227           break;
    228         case 'U': m_lastErrorString = "Access failed, RF field is off";
    229           break;
    230         default: m_lastErrorString = "Unknown error code";
    231           break;
    232         }
    233       return false;
    234     }
    235 
    236   // if we are here, then store the uid info and tag type.
    237   m_tagType = (TAG_TYPE_T)resp[2];
    238 
    239   if ((uint8_t)resp[0] == 6)
    240     m_uidLen = 4;               // 4 byte uid
    241   else
    242     m_uidLen = 7;               // 7 byte
    243 
    244   for (int i=0; i<m_uidLen; i++)
    245     m_uid.push_back(resp[i+3]);
    246 
    247   return true;
    248 }
    249 
    250 bool SM130::authenticate(uint8_t block, KEY_TYPES_T keyType, string key)
    251 {
    252   clearError();
    253 
    254   // A little sanity checking...
    255   if (keyType == KEY_TYPE_A || keyType == KEY_TYPE_B)
    256     {
    257       if (key.empty())
    258         throw std::invalid_argument(string(__FUNCTION__) +
    259                                     ": You must specify a key for type A or B");
    260       if (key.size() != 6)
    261         throw std::invalid_argument(string(__FUNCTION__) +
    262                                     ": Key size must be 6");
    263 
    264       return false; // probably not reached :)
    265     }
    266   else
    267     {
    268       // make sure the key is empty for any other key type
    269       key.clear();
    270     }
    271 
    272   string data;
    273   data.push_back(block);
    274   data.push_back(keyType);
    275   data += key;
    276 
    277   string resp = sendCommand(CMD_AUTHENTICATE, data);
    278 
    279   if (resp.empty())
    280     {
    281       cerr << __FUNCTION__ << ": failed" << endl;
    282       return false;
    283     }
    284 
    285   // response len is always 2, 'L' means auth was successful
    286   if (resp[2] != 'L')
    287     {
    288       // then we got an error of some sort, store the error code, str
    289       // and bail.
    290       m_lastErrorCode = resp[2];
    291 
    292       switch (m_lastErrorCode)
    293         {
    294         case 'N': m_lastErrorString = "No tag present, or login failed";
    295           break;
    296         case 'U': m_lastErrorString = "Login failed";
    297           break;
    298         case 'E': m_lastErrorString = "Invalid key format in EEPROM";
    299           break;
    300         default: m_lastErrorString = "Unknown error code";
    301           break;
    302         }
    303       return false;
    304     }
    305 
    306   return true;
    307 }
    308 
    309 string SM130::readBlock16(uint8_t block)
    310 {
    311   clearError();
    312 
    313   string data;
    314 
    315   data.push_back(block);
    316 
    317   string resp = sendCommand(CMD_READ16, data);
    318 
    319   if (resp.empty())
    320     {
    321       cerr << __FUNCTION__ << ": failed" << endl;
    322       return "";
    323     }
    324 
    325   if ((uint8_t)resp[0] == 2)
    326     {
    327       // then we got an error of some sort, store the error code, str
    328       // and bail.
    329       m_lastErrorCode = resp[2];
    330 
    331       switch (m_lastErrorCode)
    332         {
    333         case 'N': m_lastErrorString = "No tag present";
    334           break;
    335         case 'F': m_lastErrorString = "Read failed";
    336           break;
    337         default: m_lastErrorString = "Unknown error code";
    338           break;
    339         }
    340 
    341       return "";
    342     }
    343 
    344   // trim off the len, cmd, and block # bytes and return the rest
    345   resp.erase(0, 3);
    346   return resp;
    347 }
    348 
    349 int32_t SM130::readValueBlock(uint8_t block)
    350 {
    351   clearError();
    352 
    353   string data;
    354 
    355   data.push_back(block);
    356 
    357   string resp = sendCommand(CMD_READ_VALUE, data);
    358 
    359   if (resp.empty())
    360     {
    361       cerr << __FUNCTION__ << ": failed" << endl;
    362       return 0;
    363     }
    364 
    365   if ((uint8_t)resp[0] == 2)
    366     {
    367       // then we got an error of some sort, store the error code, str
    368       // and bail.
    369       m_lastErrorCode = resp[2];
    370 
    371       switch (m_lastErrorCode)
    372         {
    373         case 'N': m_lastErrorString = "No tag present";
    374           break;
    375         case 'F': m_lastErrorString = "Read failed";
    376           break;
    377         case 'I': m_lastErrorString = "Invalid Value Block";
    378           break;
    379         default: m_lastErrorString = "Unknown error code";
    380           break;
    381         }
    382 
    383       return 0;
    384     }
    385 
    386   int32_t rv;
    387   rv = ((uint8_t)resp[3] |
    388         ((uint8_t)resp[4] << 8) |
    389         ((uint8_t)resp[5] << 16) |
    390         ((uint8_t)resp[6] << 24));
    391 
    392   return rv;
    393 }
    394 
    395 bool SM130::writeBlock16(uint8_t block, string contents)
    396 {
    397   clearError();
    398 
    399   // A little sanity checking...
    400   if (contents.size() != 16)
    401     {
    402       throw std::invalid_argument(string(__FUNCTION__) +
    403                                   ": You must supply 16 bytes for block content");
    404 
    405       return false;
    406     }
    407 
    408   string data;
    409   data.push_back(block);
    410   data += contents;
    411 
    412   string resp = sendCommand(CMD_WRITE16, data);
    413 
    414   if (resp.empty())
    415     {
    416       cerr << __FUNCTION__ << ": failed" << endl;
    417       return false;
    418     }
    419 
    420   if ((uint8_t)resp[0] == 2)
    421     {
    422       // then we got an error of some sort, store the error code, str
    423       // and bail.
    424       m_lastErrorCode = resp[2];
    425 
    426       switch (m_lastErrorCode)
    427         {
    428         case 'F': m_lastErrorString = "Write failed";
    429           break;
    430         case 'N': m_lastErrorString = "No tag present";
    431           break;
    432         case 'U': m_lastErrorString = "Read after write failed";
    433           break;
    434         case 'X': m_lastErrorString = "Unable to read after write";
    435           break;
    436         default: m_lastErrorString = "Unknown error code";
    437           break;
    438         }
    439       return false;
    440     }
    441 
    442   return true;
    443 }
    444 
    445 bool SM130::writeValueBlock(uint8_t block, int32_t value)
    446 {
    447   clearError();
    448 
    449   string data;
    450   data.push_back(block);
    451   // put the value in, LSB first
    452   data += (value & 0xff);
    453   data += ((value >> 8) & 0xff);
    454   data += ((value >> 16) & 0xff);
    455   data += ((value >> 24) & 0xff);
    456 
    457   string resp = sendCommand(CMD_WRITE_VALUE, data);
    458 
    459   if (resp.empty())
    460     {
    461       cerr << __FUNCTION__ << ": failed" << endl;
    462       return false;
    463     }
    464 
    465   if ((uint8_t)resp[0] == 2)
    466     {
    467       // then we got an error of some sort, store the error code, str
    468       // and bail.
    469       m_lastErrorCode = resp[2];
    470 
    471       switch (m_lastErrorCode)
    472         {
    473         case 'F': m_lastErrorString = "Read failed during verification";
    474           break;
    475         case 'N': m_lastErrorString = "No tag present";
    476           break;
    477         case 'I': m_lastErrorString = "Invalid value block";
    478           break;
    479         default: m_lastErrorString = "Unknown error code";
    480           break;
    481         }
    482       return false;
    483     }
    484 
    485   return true;
    486 }
    487 
    488 bool SM130::writeBlock4(uint8_t block, string contents)
    489 {
    490   clearError();
    491 
    492   // A little sanity checking...
    493   if (contents.size() != 4)
    494     {
    495       throw std::invalid_argument(string(__FUNCTION__) +
    496                                   ": You must supply 4 bytes for block content");
    497 
    498       return false;
    499     }
    500 
    501   string data;
    502   data.push_back(block);
    503   data += contents;
    504 
    505   string resp = sendCommand(CMD_WRITE4, data);
    506 
    507   if (resp.empty())
    508     {
    509       cerr << __FUNCTION__ << ": failed" << endl;
    510       return false;
    511     }
    512 
    513   if ((uint8_t)resp[0] == 2)
    514     {
    515       // then we got an error of some sort, store the error code, str
    516       // and bail.
    517       m_lastErrorCode = resp[2];
    518 
    519       switch (m_lastErrorCode)
    520         {
    521         case 'F': m_lastErrorString = "Write failed";
    522           break;
    523         case 'N': m_lastErrorString = "No tag present";
    524           break;
    525         case 'U': m_lastErrorString = "Read after write failed";
    526           break;
    527         case 'X': m_lastErrorString = "Unable to read after write";
    528           break;
    529         default: m_lastErrorString = "Unknown error code";
    530           break;
    531         }
    532       return false;
    533     }
    534 
    535   return true;
    536 }
    537 
    538 bool SM130::writeKey(uint8_t eepromSector, KEY_TYPES_T keyType, string key)
    539 {
    540   clearError();
    541 
    542   // A little sanity checking...
    543   eepromSector &= 0x0f; // Only 0x00-0x0f is valid
    544 
    545   if (!(keyType == KEY_TYPE_A || keyType == KEY_TYPE_B))
    546     {
    547       throw std::invalid_argument(string(__FUNCTION__) +
    548                                   ": Key type must be A or B");
    549 
    550       return false;
    551     }
    552 
    553   if (key.size() != 6)
    554     {
    555       throw std::invalid_argument(string(__FUNCTION__) +
    556                                   ": Key must be 6 bytes");
    557 
    558       return false;
    559     }
    560 
    561   string data;
    562   data.push_back(eepromSector);
    563   data += keyType;
    564   data += key;
    565 
    566   string resp = sendCommand(CMD_WRITE_KEY, data);
    567 
    568   if (resp.empty())
    569     {
    570       cerr << __FUNCTION__ << ": failed" << endl;
    571       return false;
    572     }
    573 
    574   // reponse len is always 2
    575   if ((uint8_t)resp[2] != 'L')
    576     {
    577       // then we got an error of some sort, store the error code, str
    578       // and bail.
    579       m_lastErrorCode = resp[2];
    580 
    581       switch (m_lastErrorCode)
    582         {
    583         case 'N': m_lastErrorString = "Write master key failed";
    584           break;
    585         default: m_lastErrorString = "Unknown error code";
    586           break;
    587         }
    588       return false;
    589     }
    590 
    591   return true;
    592 }
    593 
    594 int32_t SM130::adjustValueBlock(uint8_t block, int32_t value, bool incr)
    595 {
    596   clearError();
    597 
    598   string data;
    599   data.push_back(block);
    600   // put the value in, LSB first
    601   data += (value & 0xff);
    602   data += ((value >> 8) & 0xff);
    603   data += ((value >> 16) & 0xff);
    604   data += ((value >> 24) & 0xff);
    605 
    606   string resp = sendCommand(((incr) ? CMD_INC_VALUE : CMD_DEC_VALUE), data);
    607 
    608   if (resp.empty())
    609     {
    610       cerr << __FUNCTION__ << ": failed" << endl;
    611       return 0;
    612     }
    613 
    614   if ((uint8_t)resp[0] == 2)
    615     {
    616       // then we got an error of some sort, store the error code, str
    617       // and bail.
    618       m_lastErrorCode = resp[2];
    619 
    620       switch (m_lastErrorCode)
    621         {
    622         case 'F': m_lastErrorString = "Read failed during verification";
    623           break;
    624         case 'N': m_lastErrorString = "No tag present";
    625           break;
    626         case 'I': m_lastErrorString = "Invalid value block";
    627           break;
    628         default: m_lastErrorString = "Unknown error code";
    629           break;
    630         }
    631       return 0;
    632     }
    633 
    634   // now unpack the new value, LSB first
    635   int32_t rv;
    636   rv = ((uint8_t)resp[3] |
    637         ((uint8_t)resp[4] << 8) |
    638         ((uint8_t)resp[5] << 16) |
    639         ((uint8_t)resp[6] << 24));
    640 
    641   return rv;
    642 }
    643 
    644 bool SM130::setAntennaPower(bool on)
    645 {
    646   clearError();
    647 
    648   string resp = sendCommand(CMD_ANTENNA_POWER, "");
    649 
    650   if (resp.empty())
    651     {
    652       cerr << __FUNCTION__ << ": failed" << endl;
    653       return false;
    654     }
    655 
    656   return true;
    657 }
    658 
    659 uint8_t SM130::readPorts()
    660 {
    661   clearError();
    662 
    663   string resp = sendCommand(CMD_READ_PORT, "");
    664 
    665   if (resp.empty())
    666     {
    667       cerr << __FUNCTION__ << ": failed" << endl;
    668       return 0;
    669     }
    670 
    671   // only the first 2 bits are valid
    672   return (resp[2] & 3);
    673 }
    674 
    675 bool SM130::writePorts(uint8_t val)
    676 {
    677   clearError();
    678 
    679   // only the first 2 bits are valid
    680   val &= 3;
    681 
    682   string data;
    683   data.push_back(val);
    684 
    685   string resp = sendCommand(CMD_WRITE_PORT, data);
    686 
    687   if (resp.empty())
    688     {
    689       cerr << __FUNCTION__ << ": failed" << endl;
    690       return false;
    691     }
    692 
    693   return true;
    694 }
    695 
    696 bool SM130::haltTag()
    697 {
    698   clearError();
    699 
    700   string resp = sendCommand(CMD_HALT_TAG, "");
    701 
    702   if (resp.empty())
    703     {
    704       cerr << __FUNCTION__ << ": failed" << endl;
    705       return false;
    706     }
    707 
    708   // reponse len is always 2
    709   if ((uint8_t)resp[2] != 'L')
    710     {
    711       // then we got an error of some sort, store the error code, str
    712       // and bail.
    713       m_lastErrorCode = resp[2];
    714 
    715       switch (m_lastErrorCode)
    716         {
    717         case 'U': m_lastErrorString = "Can not halt, RF field is off";
    718           break;
    719         default: m_lastErrorString = "Unknown error code";
    720           break;
    721         }
    722       return false;
    723     }
    724 
    725   return true;
    726 }
    727 
    728 bool SM130::setSM130BaudRate(int baud)
    729 {
    730   clearError();
    731 
    732   uint8_t newBaud;
    733 
    734   switch (baud)
    735     {
    736     case 9600: newBaud = 0x00;
    737       break;
    738     case 19200: newBaud = 0x01;
    739       break;
    740     case 38400: newBaud = 0x02;
    741       break;
    742     case 57600: newBaud = 0x03;
    743       break;
    744     case 115200: newBaud = 0x04;
    745       break;
    746     default:
    747       throw std::invalid_argument(string(__FUNCTION__) +
    748                                   ": Invalid SM130 baud rate specified");
    749     }
    750 
    751   // WARNING: this is a dangerous command
    752   int oldBaud = m_baud;
    753   m_baud = baud;
    754 
    755   string data;
    756   data.push_back(newBaud);
    757 
    758   string resp = sendCommand(CMD_SET_BAUD, data);
    759 
    760   if (resp.empty())
    761     {
    762       cerr << __FUNCTION__ << ": failed" << endl;
    763       cerr << __FUNCTION__ << ": restoring old baud rate" << endl;
    764 
    765       setBaudRate(oldBaud);
    766       return false;
    767     }
    768 
    769   // otherwise assume success, possibly incorrectly
    770   return true;
    771 }
    772 
    773 bool SM130::sleep()
    774 {
    775   clearError();
    776 
    777   string resp = sendCommand(CMD_SLEEP, "");
    778 
    779   if (resp.empty())
    780     {
    781       cerr << __FUNCTION__ << ": failed" << endl;
    782       return false;
    783     }
    784 
    785   return true;
    786 }
    787 
    788 string SM130::string2HexString(string input)
    789 {
    790   static const char* const lut = "0123456789abcdef";
    791   size_t len = input.size();
    792 
    793   string output;
    794   output.reserve(3 * len);
    795   for (size_t i = 0; i < len; ++i)
    796     {
    797       const unsigned char c = input[i];
    798       output.push_back(lut[c >> 4]);
    799       output.push_back(lut[c & 15]);
    800       output.push_back(' ');
    801     }
    802 
    803   return output;
    804 }
    805 
    806 void SM130::initClock()
    807 {
    808   gettimeofday(&m_startTime, NULL);
    809 }
    810 
    811 uint32_t SM130::getMillis()
    812 {
    813   struct timeval elapsed, now;
    814   uint32_t elapse;
    815 
    816   // get current time
    817   gettimeofday(&now, NULL);
    818 
    819   // compute the delta since m_startTime
    820   if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 )
    821     {
    822       elapsed.tv_usec += 1000000;
    823       elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1;
    824     }
    825   else
    826     {
    827       elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec;
    828     }
    829 
    830   elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000));
    831 
    832   // never return 0
    833   if (elapse == 0)
    834     elapse = 1;
    835 
    836   return elapse;
    837 }
    838 
    839 bool SM130::waitForTag(uint32_t timeout)
    840 {
    841   initClock();
    842 
    843   do
    844     {
    845       if (select())
    846         {
    847           // success
    848           return true;
    849         }
    850       else
    851         {
    852           // If there was an error, fail if it's anything other than a
    853           // tag not present
    854           if (getLastErrorCode() != 'N')
    855             return false;
    856 
    857           // otherwise, sleep for 100ms and try again
    858           usleep(100000);
    859         }
    860     } while (getMillis() <= timeout);
    861 
    862   return false;
    863 }
    864 
    865 string SM130::tag2String(TAG_TYPE_T tag)
    866 {
    867   switch (tag)
    868     {
    869     case TAG_MIFARE_ULTRALIGHT: return "MiFare Ultralight";
    870     case TAG_MIFARE_1K:         return "MiFare 1K";
    871     case TAG_MIFARE_4K:         return "MiFare 4K";
    872     case TAG_UNKNOWN:           return "Unknown Tag Type";
    873     default:                    return "Invalid Tag Type";
    874     }
    875 }
    876 
    877 
    878 
    879