Files
upm/src/sm130/sm130.cxx
Noel Eck 922e0cc26b cpp_headers: Renamed C++ headers from .h -> .hpp
To make room for UPM C and C++ sensor code to coexist, all UPM
C++ headers have been renamed from h -> hpp.  This commit contains
updates to documentation, includes, cmake collateral, examples, and
swig interface files.

    * Renamed all cxx/cpp header files which contain the string
    'copyright intel' from .h -> .hpp (if not already hpp).

    * Replaced all references to .h with .hpp in documentation,
    source files, cmake collateral, example code, and swig interface
    files.

    * Replaced cmake variable module_h with module_hpp.

    * Intentionally left upm.h since this file currently does not
    contain code (documentation only).

Signed-off-by: Noel Eck <noel.eck@intel.com>
2016-04-28 14:00:54 -07:00

879 lines
19 KiB
C++

/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2015 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdexcept>
#include "sm130.hpp"
using namespace upm;
using namespace std;
// Uncomment the below to see packaet data sent and received from the SM130
// #define SM130_DEBUG
static const int defaultDelay = 1000; // ms for read
static const int maxLen = 64; // max number of bytes to read
SM130::SM130(int uart, int reset) :
m_uart(uart), m_gpioReset(reset)
{
m_tagType = TAG_NONE;
m_uidLen = 0;
m_uid.clear();
clearError();
initClock();
m_gpioReset.dir(mraa::DIR_OUT);
m_gpioReset.write(0);
}
SM130::~SM130()
{
}
mraa::Result SM130::setBaudRate(int baud)
{
m_baud = baud;
return m_uart.setBaudRate(baud);
}
string SM130::sendCommand(CMD_T cmd, string data)
{
uint8_t cksum = 0;
string command;
// for uart, we need to add the sync bytes, 0xff, 0x00
command.push_back(0xff);
command.push_back(0x00);
// compute the length - command + data
uint8_t len = 1; // command
if (!data.empty())
len += data.size();
command.push_back(len);
cksum += len;
// now the command
command.push_back(cmd);
cksum += cmd;
// now the data if any
if (!data.empty())
{
for (int i=0; i<data.size(); i++)
{
command.push_back(data[i]);
cksum += (uint8_t)data[i];
}
}
// now add the checksum
command.push_back(cksum);
#ifdef SM130_DEBUG
cerr << "CMD: " << string2HexString(command) << endl;
#endif // SM130_DEBUG
// send it
m_uart.writeStr(command);
// if the command is SET_BAUD, then switch to the new baudrate here
// before attempting to read the response (and hope it worked).
if (cmd == CMD_SET_BAUD)
{
usleep(100000); // 100ms
setBaudRate(m_baud);
}
// now wait for a response
if (!m_uart.dataAvailable(defaultDelay))
{
cerr << __FUNCTION__ << ": timeout waiting for response" << endl;
return "";
}
string resp = m_uart.readStr(maxLen);
#ifdef SM130_DEBUG
cerr << "RSP: " << string2HexString(resp) << endl;
#endif // SM130_DEBUG
if (!((uint8_t)resp[0] == 0xff && (uint8_t)resp[1] == 0x00))
{
cerr << __FUNCTION__ << ": invalid packet header" << endl;
return "";
}
// check size - 2 header bytes + len + cksum.
if (resp.size() != ((uint8_t)resp[2] + 2 + 1 + 1))
{
cerr << __FUNCTION__ << ": invalid packet length, expected "
<< int((uint8_t)resp[2] + 2 + 1 + 1)
<< ", got " << resp.size() << endl;
return "";
}
// verify the cksum
cksum = 0;
for (int i=2; i<(resp.size() - 1); i++)
cksum += (uint8_t)resp[i];
if (cksum != (uint8_t)resp[resp.size() - 1])
{
cerr << __FUNCTION__ << ": invalid checksum, expected "
<< int(cksum) << ", got " << (uint8_t)resp[resp.size()-1] << endl;
return "";
}
// we could also verify that the command code returned was for the
// command submitted...
// now, remove the 2 header bytes and the checksum, leave the length
// and command.
resp.erase(resp.size() - 1, 1); // cksum
resp.erase(0, 2); // header bytes
// return the rest
return resp;
}
string SM130::getFirmwareVersion()
{
clearError();
string resp = sendCommand(CMD_VERSION, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return "";
}
// delete the len and cmd, return the rest
resp.erase(0, 2);
return resp;
}
bool SM130::reset()
{
clearError();
string resp = sendCommand(CMD_RESET, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
return true;
}
void SM130::hardwareReset()
{
m_gpioReset.write(1);
usleep(100000);
m_gpioReset.write(0);
}
bool SM130::select()
{
clearError();
m_tagType = TAG_NONE;
m_uidLen = 0;
m_uid.clear();
string resp = sendCommand(CMD_SELECT_TAG, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'N': m_lastErrorString = "No tag present";
break;
case 'U': m_lastErrorString = "Access failed, RF field is off";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
// if we are here, then store the uid info and tag type.
m_tagType = (TAG_TYPE_T)resp[2];
if ((uint8_t)resp[0] == 6)
m_uidLen = 4; // 4 byte uid
else
m_uidLen = 7; // 7 byte
for (int i=0; i<m_uidLen; i++)
m_uid.push_back(resp[i+3]);
return true;
}
bool SM130::authenticate(uint8_t block, KEY_TYPES_T keyType, string key)
{
clearError();
// A little sanity checking...
if (keyType == KEY_TYPE_A || keyType == KEY_TYPE_B)
{
if (key.empty())
throw std::invalid_argument(string(__FUNCTION__) +
": You must specify a key for type A or B");
if (key.size() != 6)
throw std::invalid_argument(string(__FUNCTION__) +
": Key size must be 6");
return false; // probably not reached :)
}
else
{
// make sure the key is empty for any other key type
key.clear();
}
string data;
data.push_back(block);
data.push_back(keyType);
data += key;
string resp = sendCommand(CMD_AUTHENTICATE, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
// response len is always 2, 'L' means auth was successful
if (resp[2] != 'L')
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'N': m_lastErrorString = "No tag present, or login failed";
break;
case 'U': m_lastErrorString = "Login failed";
break;
case 'E': m_lastErrorString = "Invalid key format in EEPROM";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
string SM130::readBlock16(uint8_t block)
{
clearError();
string data;
data.push_back(block);
string resp = sendCommand(CMD_READ16, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return "";
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'N': m_lastErrorString = "No tag present";
break;
case 'F': m_lastErrorString = "Read failed";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return "";
}
// trim off the len, cmd, and block # bytes and return the rest
resp.erase(0, 3);
return resp;
}
int32_t SM130::readValueBlock(uint8_t block)
{
clearError();
string data;
data.push_back(block);
string resp = sendCommand(CMD_READ_VALUE, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return 0;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'N': m_lastErrorString = "No tag present";
break;
case 'F': m_lastErrorString = "Read failed";
break;
case 'I': m_lastErrorString = "Invalid Value Block";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return 0;
}
int32_t rv;
rv = ((uint8_t)resp[3] |
((uint8_t)resp[4] << 8) |
((uint8_t)resp[5] << 16) |
((uint8_t)resp[6] << 24));
return rv;
}
bool SM130::writeBlock16(uint8_t block, string contents)
{
clearError();
// A little sanity checking...
if (contents.size() != 16)
{
throw std::invalid_argument(string(__FUNCTION__) +
": You must supply 16 bytes for block content");
return false;
}
string data;
data.push_back(block);
data += contents;
string resp = sendCommand(CMD_WRITE16, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'F': m_lastErrorString = "Write failed";
break;
case 'N': m_lastErrorString = "No tag present";
break;
case 'U': m_lastErrorString = "Read after write failed";
break;
case 'X': m_lastErrorString = "Unable to read after write";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
bool SM130::writeValueBlock(uint8_t block, int32_t value)
{
clearError();
string data;
data.push_back(block);
// put the value in, LSB first
data += (value & 0xff);
data += ((value >> 8) & 0xff);
data += ((value >> 16) & 0xff);
data += ((value >> 24) & 0xff);
string resp = sendCommand(CMD_WRITE_VALUE, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'F': m_lastErrorString = "Read failed during verification";
break;
case 'N': m_lastErrorString = "No tag present";
break;
case 'I': m_lastErrorString = "Invalid value block";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
bool SM130::writeBlock4(uint8_t block, string contents)
{
clearError();
// A little sanity checking...
if (contents.size() != 4)
{
throw std::invalid_argument(string(__FUNCTION__) +
": You must supply 4 bytes for block content");
return false;
}
string data;
data.push_back(block);
data += contents;
string resp = sendCommand(CMD_WRITE4, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'F': m_lastErrorString = "Write failed";
break;
case 'N': m_lastErrorString = "No tag present";
break;
case 'U': m_lastErrorString = "Read after write failed";
break;
case 'X': m_lastErrorString = "Unable to read after write";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
bool SM130::writeKey(uint8_t eepromSector, KEY_TYPES_T keyType, string key)
{
clearError();
// A little sanity checking...
eepromSector &= 0x0f; // Only 0x00-0x0f is valid
if (!(keyType == KEY_TYPE_A || keyType == KEY_TYPE_B))
{
throw std::invalid_argument(string(__FUNCTION__) +
": Key type must be A or B");
return false;
}
if (key.size() != 6)
{
throw std::invalid_argument(string(__FUNCTION__) +
": Key must be 6 bytes");
return false;
}
string data;
data.push_back(eepromSector);
data += keyType;
data += key;
string resp = sendCommand(CMD_WRITE_KEY, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
// reponse len is always 2
if ((uint8_t)resp[2] != 'L')
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'N': m_lastErrorString = "Write master key failed";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
int32_t SM130::adjustValueBlock(uint8_t block, int32_t value, bool incr)
{
clearError();
string data;
data.push_back(block);
// put the value in, LSB first
data += (value & 0xff);
data += ((value >> 8) & 0xff);
data += ((value >> 16) & 0xff);
data += ((value >> 24) & 0xff);
string resp = sendCommand(((incr) ? CMD_INC_VALUE : CMD_DEC_VALUE), data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return 0;
}
if ((uint8_t)resp[0] == 2)
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'F': m_lastErrorString = "Read failed during verification";
break;
case 'N': m_lastErrorString = "No tag present";
break;
case 'I': m_lastErrorString = "Invalid value block";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return 0;
}
// now unpack the new value, LSB first
int32_t rv;
rv = ((uint8_t)resp[3] |
((uint8_t)resp[4] << 8) |
((uint8_t)resp[5] << 16) |
((uint8_t)resp[6] << 24));
return rv;
}
bool SM130::setAntennaPower(bool on)
{
clearError();
string resp = sendCommand(CMD_ANTENNA_POWER, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
return true;
}
uint8_t SM130::readPorts()
{
clearError();
string resp = sendCommand(CMD_READ_PORT, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return 0;
}
// only the first 2 bits are valid
return (resp[2] & 3);
}
bool SM130::writePorts(uint8_t val)
{
clearError();
// only the first 2 bits are valid
val &= 3;
string data;
data.push_back(val);
string resp = sendCommand(CMD_WRITE_PORT, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
return true;
}
bool SM130::haltTag()
{
clearError();
string resp = sendCommand(CMD_HALT_TAG, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
// reponse len is always 2
if ((uint8_t)resp[2] != 'L')
{
// then we got an error of some sort, store the error code, str
// and bail.
m_lastErrorCode = resp[2];
switch (m_lastErrorCode)
{
case 'U': m_lastErrorString = "Can not halt, RF field is off";
break;
default: m_lastErrorString = "Unknown error code";
break;
}
return false;
}
return true;
}
bool SM130::setSM130BaudRate(int baud)
{
clearError();
uint8_t newBaud;
switch (baud)
{
case 9600: newBaud = 0x00;
break;
case 19200: newBaud = 0x01;
break;
case 38400: newBaud = 0x02;
break;
case 57600: newBaud = 0x03;
break;
case 115200: newBaud = 0x04;
break;
default:
throw std::invalid_argument(string(__FUNCTION__) +
": Invalid SM130 baud rate specified");
}
// WARNING: this is a dangerous command
int oldBaud = m_baud;
m_baud = baud;
string data;
data.push_back(newBaud);
string resp = sendCommand(CMD_SET_BAUD, data);
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
cerr << __FUNCTION__ << ": restoring old baud rate" << endl;
setBaudRate(oldBaud);
return false;
}
// otherwise assume success, possibly incorrectly
return true;
}
bool SM130::sleep()
{
clearError();
string resp = sendCommand(CMD_SLEEP, "");
if (resp.empty())
{
cerr << __FUNCTION__ << ": failed" << endl;
return false;
}
return true;
}
string SM130::string2HexString(string input)
{
static const char* const lut = "0123456789abcdef";
size_t len = input.size();
string output;
output.reserve(3 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
output.push_back(' ');
}
return output;
}
void SM130::initClock()
{
gettimeofday(&m_startTime, NULL);
}
uint32_t SM130::getMillis()
{
struct timeval elapsed, now;
uint32_t elapse;
// get current time
gettimeofday(&now, NULL);
// compute the delta since m_startTime
if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 )
{
elapsed.tv_usec += 1000000;
elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1;
}
else
{
elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec;
}
elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000));
// never return 0
if (elapse == 0)
elapse = 1;
return elapse;
}
bool SM130::waitForTag(uint32_t timeout)
{
initClock();
do
{
if (select())
{
// success
return true;
}
else
{
// If there was an error, fail if it's anything other than a
// tag not present
if (getLastErrorCode() != 'N')
return false;
// otherwise, sleep for 100ms and try again
usleep(100000);
}
} while (getMillis() <= timeout);
return false;
}
string SM130::tag2String(TAG_TYPE_T tag)
{
switch (tag)
{
case TAG_MIFARE_ULTRALIGHT: return "MiFare Ultralight";
case TAG_MIFARE_1K: return "MiFare 1K";
case TAG_MIFARE_4K: return "MiFare 4K";
case TAG_UNKNOWN: return "Unknown Tag Type";
default: return "Invalid Tag Type";
}
}