Integración de Lectores MSR605 con Python - Guía Práctica

Cómo controlar lectores de tarjetas magnéticas MSR605 desde Python usando comunicación serial y comandos ESC

Integración de Lectores MSR605 con Python - Guía Práctica

El MSR605 es un lector/grabador de tarjetas magnéticas versátil que se comunica vía puerto serial (RS-232 o USB emulando serial). En esta guía te muestro cómo implementar una clase Python completa para controlar todas sus funciones.


1. ¿Qué es el MSR605?

El MSR605 es un dispositivo de lectura/escritura de tarjetas magnéticas que:

  • Lee y escribe en 3 tracks (pistas magnéticas)
  • Soporta estándar ISO7811
  • Se comunica a través de puerto serial (9600 baud)
  • Incluye LEDs de estado (rojo, amarillo, verde)
  • Realiza pruebas de diagnóstico (sensor, RAM, comunicación)

Aplicaciones comunes:

  • Control de acceso (tarjetas de hotel)
  • Lectores POS
  • Sistemas de autenticación heredados
  • Validación de tarjetas de identificación.

2. Estructura del Código

Clase Commands

Define todos los comandos del MSR605 como métodos que retornan cadenas hexadecimales:

class Commands(object):
    def __init__(self):
        self.ESC = '\x1B'  # Carácter de escape (0x1B)

    def reset(self):
        """Reinicia el MSR605 al estado inicial"""
        return self.ESC + '\x61'

    def read_iso(self):
        """Lee tarjeta y responde con datos decodificados (ISO7811)"""
        return self.ESC + '\x72'

    def write_iso(self):
        """Escribe datos en tarjeta (ISO7811)"""
        return self.ESC + '\x77'

Todos los comandos siguen el patrón: ESC + código_hexadecimal

Clase MSR

Extiende serial.Serial para comunicación con el dispositivo:

class MSR(serial.Serial):
    def __init__(self, dev, test=True, timeout=10):
        super(MSR, self).__init__(dev, 9600, 8, serial.PARITY_NONE, timeout=timeout)
        self.comando = Commands()
        self._send_command(self.comando.reset())
        self._send_command(self.comando.set_low_co())  # Modo de escritura (bajo coercitivo)
        if test:
            self._send_command(self.comando.communication_test())

3. Tabla de Comandos Principales

ComandoCódigoFunciónRespuesta
reset1B61Reinicia el dispositivoNinguna
read_iso1B72Lee tarjeta completa[DataBlock][StatusByte]
write_iso1B77Escribe en tarjeta[StatusByte]
communication_test1B65Verifica conexióny (1B79)
sensor_test1B86Test sensor de tarjeta0 (1B30) si OK
ram_test1B87Test memoria RAM0 OK o A Fallo
get_device_model1B74Obtiene modelo[Model]S
get_firmware_version1B76Versión firmware[version]

4. Leer Datos de la Tarjeta

El método leer() captura los 3 tracks de la tarjeta:

def leer(self):
    """
    Lee todos los tracks de la tarjeta y retorna una tupla (track1, track2, track3)
    """
    self._send_command(self.comando.read_iso())
    
    # Cada track termina con su marcador
    track1 = self._read_until(self.tracks.get('1'))[:-2]
    track2 = self._read_until(self.tracks.get('2'))[:-2]
    track3 = self._read_until('\x1C')[:-1]
    
    # Verifica el status byte
    _, status = self.read(2)
    if status == '\x31':
        return "error de lectura"
    
    return track1, track2, track3

Estructura de Tracks

  • Track 1: Datos completos = nombre + número de tarjeta + fecha expiración
  • Track 2: Datos mínimos = número de tarjeta + fecha expiración (usado en POS)
  • Track 3: Datos adicionales (no siempre presente)

Ejemplo de lectura:

Track1: MEDINA/MILTON^2500123456789?
Track2: 2500123456789=2512101002934891234?
Track3: (vacío o datos adicionales)

5. Escribir en la Tarjeta

El método escribir_tracks() graba datos en la tarjeta:

def escribir_tracks(self, t1="", t2="", t3=""):
    """
    Escribe en los tracks especificados
    
    :param t1: Datos Track 1 (SOLO MAYÚSCULAS)
    :param t2: Datos Track 2 (SOLO MAYÚSCULAS)
    :param t3: Datos Track 3 (SOLO MAYÚSCULAS)
    :return: True si éxito, False si fallo
    """
    self._send_command(self.comando.set_hi_co())  # Modo escritura Hi-Co
    
    # Construye el bloque de datos
    data = "\x1B\x77\x1B\x73\x1B\x01" + t1 + "\x1B\x02" + t2 + "\x1B\x03" + t3 + '\x3F\x1C'
    self._send_command(data)
    
    # Lee el byte de estado (8 bytes de respuesta)
    _, _, _, _, _, _, _, self.estado = self.read(8)
    
    return self.estado == '\x30'  # 0x30 = éxito

Consideraciones Importantes

⚠️ Mayúsculas requeridas: El MSR605 solo grabará caracteres en mayúsculas. Minúsculas aparecerán como espacios en blanco.

⚠️ Formato Track 1 estándar:

[NOMBRE/APELLIDO]^[NUMERO TARJETA]?

⚠️ Formato Track 2 estándar:

[NUMERO TARJETA]=[FECHA EXPIRACION][SERVICIO CODE][DISCRETIONARY DATA]?

6. Borrar Datos de Tarjeta

def borrar_tracks(self, tracks):
    """
    Borra pistas específicas
    
    :param tracks: String con tracks a borrar
                   '1', '2', '3', '12', '13', '23', '123'
    """
    self._send_command(self.comando.set_hi_co())
    self._send_command(self.comando.erase_card(), self.tracks.get(tracks))

Mapeo de SelectByte:

00000001 (0x01) → Track 1 solo
00000010 (0x02) → Track 2 solo
00000100 (0x04) → Track 3 solo
00000011 (0x03) → Tracks 1 & 2
00000101 (0x05) → Tracks 1 & 3
00000110 (0x06) → Tracks 2 & 3
00000111 (0x07) → Todos los tracks

7. Control de LEDs

El MSR605 tiene 3 LEDs que se pueden controlar programáticamente:

self._send_command(self.comando.all_leds_off())    # Apaga todos
self._send_command(self.comando.all_leds_on())     # Enciende todos
self._send_command(self.comando.green_led_on())    # Verde (éxito)
self._send_command(self.comando.yellow_led_on())   # Amarillo (espera)
self._send_command(self.comando.red_led_on())      # Rojo (error)

Ejemplo práctico para feedback:

def leer_con_feedback(self):
    self._send_command(self.comando.yellow_led_on())  # Espera...
    try:
        datos = self.leer()
        self._send_command(self.comando.green_led_on())  # Éxito
        return datos
    except Exception as e:
        self._send_command(self.comando.red_led_on())  # Error
        raise e

8. Métodos Auxiliares

_send_command()

def _send_command(self, command, *args):
    """Envía comando al dispositivo"""
    self.flushInput()
    self.flushOutput()
    self.write(command + ''.join(args))
    self.flush()

Limpia buffers antes de enviar para evitar datos residuales.

_read_until()

def _read_until(self, end):
    """Lee datos hasta encontrar el marcador final"""
    data = ''
    while True:
        data += self.read(1)
        if data.endswith(end):
            return data

Bloqueante hasta encontrar el terminador especificado.


9. Ejemplo de Uso Completo

from msr_controller import MSR

# Inicializar
msr = MSR('/dev/ttyUSB0', test=True)

# Leer tarjeta
try:
    track1, track2, track3 = msr.leer()
    print(f"Track 1: {track1}")
    print(f"Track 2: {track2}")
except Exception as e:
    print(f"Error: {e}")

# Escribir en tarjeta
exito = msr.escribir_tracks(
    t1="NEWUSER/JOHN^1234567890123?",
    t2="1234567890123=2512101002934891234?"
)

if exito:
    print("Tarjeta grabada exitosamente")
else:
    print("Error al grabar tarjeta")

# Verificación
msr._send_command(msr.comando.communication_test())
respuesta = msr.read(2)
print(f"Test comunicación: {respuesta}")

msr.close()

10. Troubleshooting

ProblemaCausa ProbableSolución
Comando no respondePuerto serial incorrectoVerificar con lsusb o Device Manager
Lee datos vacíosTarjeta no pasa correctamenteLimpiar cabezal sensor
Error en escrituraTarjeta bloqueada Hi-CoUsar set_low_co() para tarjetas de prueba
Minúsculas aparecen como espaciosFormato - MSR605 requiere mayúsculasConvertir input a .upper()
RAM test fallaHardware dañadoReiniciar con reset()
Timeout en lecturaSensor no detecta tarjetaRevisar conexión física, limpiar sensor

11. Configuración de Coercitividad

El MSR605 soporta dos modos de escritura:

Hi-Coercitivo (HC): Tarjetas de larga duración (acero)

self._send_command(self.comando.set_hi_co())

Low-Coercitivo (LC): Tarjetas temporales (hoteles, pruebas)

self._send_command(self.comando.set_low_co())

Verificar estado actual:

self._send_command(self.comando.get_hi_co_or_low_costatus())
# Retorna: <ESC>H (Hi-Co) o <ESC>L (Low-Co)

Notas Finales

  • Puerto Serial: Generalmente /dev/ttyUSB0 (Linux), COM3 (Windows)
  • Baud Rate: Siempre 9600
  • Timeout: Mínimo 10 segundos para operaciones de lectura
  • Limpieza: Limpiar el cabezal magnético regularmente con alcohol isopropílico
  • Datos sensibles: No almacenar datos de tarjetas en texto plano