Using SonMicro SM130 RFID Modules with Arduino

It took me ages to find an inexpensive RFID module that would work with the Philips Mifare system. Most readers on the market were USB devices costing £50. Completely unsuitable for use with the Arduino or other inexpensive microprocessors.

Then I discovered SonMicro who make a whole range of useful RFID modules. The SM130 is a simple 13.56Mhz RFID reader that reads and writes to cards. Everything you need apart from the antenna is included so you just wire up power and communicate with the device using serial or I2C.

Using I2c requires a firmware update available from the manufacturer. Communication with the device is via a simple protocol described in the datasheet.

Sparkfun is now selling the devices which makes them easier to get hold of. I ordered my directly from SonMicro who were excellent and the devices arrived from Turkey to the UK in, from memory, less than two days. However, there is a minimum order value, shipping fees and import taxes to pay so Sparkfun is going to make this much more accessible to a hobbyist who’s not ordering large quantities.

Below is the Arduino code I used for playing with the device’s functionality:

#include <NewSoftSerial.h>

// rfid module is connected to pins 2 and 3 of the Arduino
NewSoftSerial rfid(2, 3);
int val;
int ANTENNA_OFF[] = {
  0x90, 0x00 };
int ANTENNA_ON[] = {
  0x90, 0x01 };
int FIRMWARE[] = {
  0x81 };
int HALT[] = {
  0x93 };
int READ_INPUT[] = {
  0x91 };
int READ_I2C[] = {
  0x9C };

int RESET[] = {
  0x80 };
int SEEK[] = {
  0x82 };
int SELECT[] = {
  0x83 };
int SLEEP[] = {
  0x96 };

bool antenna = true;
bool output_bit1 = true;
bool output_bit2 = true;
int count = 0;
char inputBuffer[20];

void setup()
{
  Serial.begin(57600);

  // set the data rate to 19200 to talk to the RFID module
  rfid.begin(19200);
  //send_command(SEEK, 3);
  Serial.print("Initalised\n");
}

// Send a command to the device to put into SEEK MODE
// TODO: Update to automatically calculate length and checksum
void send_command(int command[], int length) {
  // Send header
  rfid.print((char)0xFF);
  rfid.print((char)0x00);

  rfid.print((char)length);
  int checksum = length;
  for (int idx = 0; idx < length; idx++) {
    rfid.print((char)command[idx]);
    checksum += command[idx];
    //   Serial.print(command[idx], HEX);
  }
  checksum = checksum % 0x100;
  // checksum = 0x88; // testing purposes see 5.5 of manual - AUTHENTICATE
  rfid.print((char)checksum);
  /* Serial.print("Sending checksum: ");
   Serial.print(checksum, HEX);
   Serial.println();*/
}

void process_antenna_command(int buffer[], int length) {
  if (length == 2) {
    switch (buffer[1]) {
    case 0x0:
      Serial.println("Antenna OFF");
      break;
    case 0x1:
      Serial.println("Antenna ON");
      break;
    default:
      Serial.println("Invalid ANTENNA");
      break;
    }
  }
  else
  {
    Serial.println("Invalid ANTENNA");
  }
}

void process_firmware_command(int buffer[], int length) {
  Serial.print("RFID Firmware: ");
  for(int idx=0; idx < length; idx++) {
    Serial.print(buffer[idx], BYTE);
  }
  Serial.println();
}

void process_read_input(int buffer[], int length) {
  if (length == 2) {
    Serial.print("INPUT1 ");
    if (buffer[1] && 0x01) {
      Serial.println("ON");
    }
    else
    {
      Serial.println("OFF");
    }
    Serial.print("INPUT2 ");
    if (buffer[1] && 0x02) {
      Serial.println("ON");
    }
    else
    {
      Serial.println("OFF");
    }
  }
  else
  {
    Serial.println("Invalid READ INPUT");
  }
}

void process_write_value_block(int buffer[], int length) {
  switch (buffer[0]) {
  case 0x87:
    Serial.print("Read Value Block");
    break;
  case 0x8a:
    Serial.print("Write Value Block");
    break;
  case 0x8d:
    Serial.print("Increment Value Block");
    break;
  case 0x8e:
    Serial.print("Decrement Value Block");
    break;
  }

  if (length == 2) {
    // Error
    switch (buffer[1]) {
    case 0x4E:
      Serial.println(" - No Tag");
      break;
    case 0x49:
      Serial.println(" - Invalid Value Block");
      break;
    case 0x46:
      Serial.println(" - Read Failed during verification");
      break;
    }
  }
  else
  {
    // Sucess
    Serial.print(" successfully written to block: ");
    Serial.println(buffer[1], HEX);
    Serial.print("Value: ");
    for (int idx = 2; idx < length; idx++)
    {
      Serial.print(buffer[idx], HEX);
    }
    Serial.println();
  }

}

void process_seek_command(int buffer[], int length) {
  if (length == 2) {
    // Seek command has been received by module
    switch (buffer[1]) {
    case 0x4C:
      Serial.println("SEEK in progress");
      break;
    case 0x4E:
      Serial.println("SELECT - No Tag");
      break;
    case 0x55:
      Serial.println("SEEK in progress but RF Field is OFF");
      break;
    }
  }
  else
  {
    // Card has appeared within range
    // Output card type
    switch (buffer[1])
    {
      Serial.print("Mifare ");
    case 0x01:
      Serial.print("Ultralight");
      break;
    case 0x02:
      Serial.print("Standard 1K");
      break;
    case 0x03:
      Serial.print("Classic 4K");
      break;
    default:
      Serial.print("Unknown");
      break;
    }
    Serial.print(" card found, serial #");
    for (int idx = length - 1; idx > 1; idx--) {
      Serial.print(buffer[idx], HEX);
      Serial.print(" ");
    }
    // Put the RFID module into SEEK mode again
    //send_command(SEEK, 1);
  }
  Serial.print("\n");
}

void process_authenticate(int buffer[], int length) {
  if (length == 2) {
    switch (buffer[1]) {
    case 0x4c:
      Serial.println("Authenticate - Login Successful");
      break;
    case 0x4e:
      Serial.println("Authenticate - No Tag Present or Login Failed");
      break;
    case 0x55:
      Serial.println("Authenticate - Login Failed");
      break;
    case 0x45:
      Serial.println("Authenticate - Invalid Key Format in E2PROM");
      break;
    default:
      Serial.println("Invalid response from AUTHENTICATE command");
      break;
    }
  }
  else
  {
    Serial.println("Invalid response from AUTHENTICATE command");
  }
}

void process_halt_command(int buffer[], int length) {
  if (length == 2) {
    switch(buffer[1]) {
    case 0x4C:
      Serial.println("PICC(tag) is halted");
      break;
    case 0x55:
      Serial.println("PICC cannot be halted due to RF Field is OFF");
      break;
    default:
      Serial.println("Invalid HALT");
      break;
    }
  }
  else {
    Serial.println("Invalid HALT");
  }
}

void process_read_i2c_address(int buffer[], int length) {
  if (length == 2) {
    Serial.print("I2C Address is: ");
    Serial.println(buffer[1], HEX);
  }
  else
  {
    Serial.println("Invalid READ I2C ADDRESS");
    for (int idx =0 ; idx < length ; idx++) {
      Serial.print(buffer[idx], HEX);
      Serial.print(' ');
      Serial.println();
    }
  }
}

void process_write_i2c_address(int buffer[], int length) {

  if (length == 2 && buffer[1] == 0x4C){
    Serial.print("I2C was written sucessfully.");
  }
  else
  {
    Serial.println("Invalid WRITE I2C ADDRESS");
  }
}

void process_write_output(int buffer[], int length) {
  if (length == 2) {
    if (buffer[1] && 0x01) {
      Serial.println("OUTPUT1 is ON");
    }
    else
    {
      Serial.println("OUTPUT1 is OFF");
    }
    if (buffer[1] && 0x02) {
      Serial.println("OUTPUT2 is ON");
    }
    else
    {
      Serial.println("OUTPUT2 is OFF");
    }
  }
  else
  {
    Serial.println("Invalid WRITE OUTPUT");
  }
}

void process_sleep_command(int buffer[], int length) {
  if (buffer[1] == 0x00) {
    Serial.println("Sleep OK");
  }
  else
  {
    Serial.println("Invalid SLEEP");
  }
}

void process_command(int buffer[], int length)
{
  switch (buffer[0]) {
  case 0x81:
    process_firmware_command(buffer, length);
    break;
  case 0x82:
  case 0x83:
    process_seek_command(buffer, length);
    break;
  case 0x85:
    process_authenticate(buffer, length);
    break;
  case 0x87:
  case 0x8a:
  case 0x8d:
  case 0x8e:
    process_write_value_block(buffer, length);
    break;
  case 0x90:
    process_antenna_command(buffer, length);
    break;
  case 0x91:
    process_read_input(buffer, length);
    break;
  case 0x92:
    process_write_output(buffer, length);
    break;
  case 0x93:
    process_halt_command(buffer, length);
    break;
  case 0x96:
    process_sleep_command(buffer, length);
    break;
  case 0x9B:
    process_write_i2c_address(buffer, length);
    break;
  case 0x9C:
    process_read_i2c_address(buffer, length);
    break;

  default:
    Serial.print("Unknown command: ");
    Serial.print(buffer[0], HEX);
    Serial.print("\n");
  }
}

void loop()
{

  if (rfid.available()) {
    val = (int)rfid.read();
    if (val == 0xFF) {
      // Serial.print("Got command\n");
      // Begin command
      val = rfid.read();
      if (val != 00) {
        Serial.print("Invalid command");
      }
      int length = rfid.read();
      int buffer[20] = {
      };
      int checksum = length;
      for (int idx = 0; idx < length; idx++) {
        buffer[idx] = rfid.read();
        checksum += buffer[idx];
      }
      int recv_checksum = rfid.read();
      checksum = checksum % 0x100;

      if (checksum != recv_checksum)
      {
        Serial.println("Invalid checksum received");
        Serial.print("Recv checksum:\t\t");
        Serial.print(recv_checksum, HEX);
        Serial.print("\n");
        Serial.print("Checksum:\t\t");
        Serial.print(checksum, HEX);
        Serial.print("\n");
        Serial.print("Length:\t\t");
        Serial.print(length, HEX);
        Serial.println();
        Serial.print("COMMAND: ");
        for (int idx = 0; idx < length; idx++) {
          Serial.print(buffer[idx], HEX);
          Serial.print(" ");
        }
        Serial.println();
      } 

      process_command(buffer, length);
    }
  }

  if (Serial.available()) {
    char userCommand = (char)Serial.read();
    if (userCommand == '*') {
      process_user_command(inputBuffer, count);
      count = 0;
    }
    else
    {
      inputBuffer[count] = userCommand;
      count++;
      if (count > 20) {
        count = 0;
      }
    }
  }
}

void command_authenticate(int block) {
  int command[3];
  command[0] = 0x85;
  command[1] = 0x01; // block
  command[2] = 0xff;
  send_command(command,3);
}

void send_write_i2c_command() {
  int command[2] = {
    0x9b, 0x42   };
  send_command(command, 2);
}

// VALUE BLOCKS
void decrement_value() {
  int command[6] = {
    0x8e, 0x01, 0x03, 0x00, 0x00, 0x00                       };
  send_command(command, 6);
}

void increment_value() {
  int command[6] = {
    0x8d, 0x01, 0x03, 0x00, 0x00, 0x00                       };
  send_command(command, 6);
}

void read_value() {
  int command[2] = {
    0x87, 0x01                       };
  send_command(command, 2);
}

void write_value(int block, long data) {
  int command[6] = {
    0x8a, 0x01, 0x02, 0x00, 0x00, 0x00                       };
  send_command(command, 6);
}

void process_user_command(char inputBuffer[], int length) {
  switch (inputBuffer[0]) {
  case 'r':
    Serial.println("Reading value in block 1");
    read_value();
    break;
  case 'd':
    Serial.println("Incrementing value in block 1 by 3");
    decrement_value();
    break;
  case 'i':
    Serial.println("Incrementing value in block 1 by 3");
    increment_value();
    break;
  case 'w':
    Serial.println("Writing value of 2 to block 1");
    write_value(0,0);
    break;
  case 'A':
    if (antenna) {
      Serial.println("Turning antenna OFF");
      antenna = false;
      send_command(ANTENNA_OFF, 2);
    }
    else
    {
      Serial.println("Turning antenna ON");
      antenna = true;
      send_command(ANTENNA_ON, 2);
    }
    break;
  case 'C':
    Serial.println("Reading I2C address");
    send_command(READ_I2C, 1);
    break;
  case 'D':
    Serial.println("Writing I2C address");
    send_write_i2c_command();
    break;

  case 'F':
    Serial.println("Sending FIRMWARE command");
    send_command(FIRMWARE, 1);
    break;

  case 'G':
    Serial.println("Sending SEEK command");
    send_command(SEEK, 1);
    break;
  case 'H':
    Serial.println("Sending HALT command");
    send_command(HALT, 1);
    break;
  case 'I':
    Serial.println("Reading INPUT ports");
    send_command(READ_INPUT, 1);
    break;
  case 'L':
    Serial.println("Sending SELECT command");
    send_command(SELECT, 1);
    break;
  case 'R':
    Serial.println("Sending RESET command");
    send_command(RESET, 1);
    break;
  case 'S':
    Serial.println("Sending SLEEP command");
    send_command(SLEEP, 1);
    break;
  case 'U':
    Serial.println("Sending AUTHENTICATE command");
    //    int block = (int)inputBuffer[1];
    //int block =5;
    command_authenticate((int)inputBuffer[1]);
    break;
  default:
    Serial.println("Invalid user input");
    break;

  }
}

3 thoughts on “Using SonMicro SM130 RFID Modules with Arduino”

  1. Are the boards compatible with pin/format compatible with arduino, or did you have to build an interface?

Comments are closed.