├── .gitignore ├── README ├── TODO ├── msr.py ├── msrtool.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A linux tool for simple usage of the MSR605 magnet stripe reader/writer 2 | 3 | 4 | USAGE: 5 | 6 | ./msrtool.py /dev/ttyUSB0 7 | 8 | 9 | You should have write access to the serial port, so you either run this as 10 | root or add yourself to the dialout group (or whatever group your linux 11 | assigns /dev/ttyUSB0 to). 12 | 13 | This is heavily based on the work from Damien Bobillot, who wrote the Python 14 | driver for the MSR605's serial interface. I only wrapped a nicer frontend around. 15 | 16 | As Damien's work is GPL, this is licensed under the terms of the GPL, too. 17 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | BPC, BPI, ISO configuration 2 | -------------------------------------------------------------------------------- /msr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # File: msr.py 4 | # Version: 1.1 5 | # Author: Damien Bobillot (damien.bobillot.2002+msr@m4x.org) 6 | # Licence: GNU GPL version 3 7 | # Compatibility: tested with python 2.7 on Mac OS X, should work with any python installations. 8 | # 9 | # Driver for the magnetic strip card reader/writer MSR605, and other versions 10 | # 11 | # june 2011 - 1.0 - First version 12 | # july 2011 - 1.1 - raw read/write, set loco/hico, set density 13 | # 14 | 15 | import time 16 | import serial 17 | 18 | # defining the core object 19 | class msr(serial.Serial): 20 | # protocol 21 | escape_code = "\x1B" 22 | end_code = "\x1C" 23 | 24 | # for set_coercivity 25 | hico=True 26 | loco=False 27 | 28 | # for set_bpi 29 | hibpi=True 30 | lobpi=False 31 | 32 | # for pack/unpack 33 | track1_map = " !\"#$%&'()*+`,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" 34 | track23_map = "0123456789:;<=>?" 35 | parity_map = [1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, \ 36 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1] 37 | # 1 = count of 1 in index is even, 0 = odd 38 | rev6bit_map = [0,32,16,48,8,40,24,56,4,36,20,52,12,44,28,60,2,34,18,50,10,42,26,58,6,38,22,54,14,46,30,62, \ 39 | 1,33,17,49,9,41,25,57,5,37,21,53,13,45,29,61,3,35,19,51,11,43,27,59,7,39,23,55,15,47,31,63] 40 | # give the reverse bitmap (6 bits) of a the index 41 | 42 | def __init__(self, dev_path): 43 | if dev_path.find("/") == -1: dev_path = "/dev/" + dev_path 44 | serial.Serial.__init__(self,dev_path,9600,8,serial.PARITY_NONE,timeout=0) 45 | self.reset() 46 | 47 | def __execute_noresult(self, command): 48 | self.write(msr.escape_code+command) 49 | time.sleep(0.1) 50 | 51 | def __execute_waitresult(self, command, timeout=10): 52 | # execute 53 | self.flushInput() 54 | self.write(msr.escape_code+command) 55 | time.sleep(0.1) 56 | 57 | # get result 58 | self.timeout=timeout 59 | result = self.read() 60 | time.sleep(0.5) 61 | if result == "": raise Exception("operation timed out") 62 | self.timeout=0 63 | result += self.read(1000) 64 | 65 | # parse result : status, result, data 66 | pos = result.rindex(msr.escape_code) 67 | return result[pos+1], result[pos+2:], result[0:pos] 68 | 69 | def reset(self): 70 | self.__execute_noresult("a") 71 | 72 | @staticmethod 73 | def __decode_isodatablock(data): 74 | # header and end 75 | if data[0:4] != msr.escape_code+"s"+msr.escape_code+"\x01": 76 | raise Exception("bad datablock : don't start with s[01]", data) 77 | if data[-2:] != "?"+msr.end_code: 78 | raise Exception("bad datablock : don't end with ?", data) 79 | 80 | # first strip 81 | strip1_start = 4 82 | strip1_end = data.index(msr.escape_code,strip1_start) 83 | if strip1_end == strip1_start: 84 | strip1_end += 2 85 | strip1 = None 86 | else: 87 | strip1 = data[strip1_start:strip1_end] 88 | 89 | # second strip 90 | strip2_start = strip1_end+2 91 | if data[strip1_end:strip2_start] != msr.escape_code+"\x02": 92 | raise Exception("bad datablock : missing [02] at position %d" % strip1_end, data) 93 | strip2_end = data.index(msr.escape_code,strip2_start) 94 | if strip2_end == strip2_start: 95 | strip2_end += 2 96 | strip2 = None 97 | else: 98 | strip2 = data[strip2_start:strip2_end] 99 | 100 | # third strip 101 | strip3_start = strip2_end+2 102 | if data[strip2_end:strip3_start] != msr.escape_code+"\x03": 103 | raise Exception("bad datablock : missing [03] at position %d" % strip2_end, data) 104 | if data[strip3_start] == msr.escape_code: 105 | strip3 = None 106 | else: 107 | strip3 = data[strip3_start:-2] 108 | 109 | return strip1, strip2, strip3 110 | 111 | @staticmethod 112 | def __encode_isodatablock(strip1, strip2, strip3): 113 | # use empty string if you don't want to set a given strip 114 | return "\x1bs\x1b\x01"+strip1+"\x1b\x02"+strip2+"\x1b\x03"+strip3+"?\x1C" 115 | 116 | @staticmethod 117 | def __decode_rawdatablock(data): 118 | # header 119 | if data[0:4] != msr.escape_code+"s"+msr.escape_code+"\x01": 120 | raise Exception("bad datablock : don't start with s[01]", data) 121 | 122 | # first strip 123 | strip1_start = 4 124 | strip1_end = strip1_start + 1 + ord(data[strip1_start]) # first byte is length 125 | strip1 = data[strip1_start+1:strip1_end] 126 | 127 | # second strip 128 | strip2_start = strip1_end+2 129 | if data[strip1_end:strip2_start] != msr.escape_code+"\x02": 130 | raise Exception("bad datablock : missing [02] at position %d" % strip1_end, data) 131 | strip2_end = strip2_start + 1 + ord(data[strip2_start]) 132 | strip2 = data[strip2_start+1:strip2_end] 133 | 134 | # third strip 135 | strip3_start = strip2_end+2 136 | if data[strip2_end:strip3_start] != msr.escape_code+"\x03": 137 | raise Exception("bad datablock : missing [03] at position %d" % strip2_end, data) 138 | strip3_end = strip3_start + 1 + ord(data[strip3_start]) 139 | strip3 = data[strip3_start+1:strip3_end] 140 | 141 | # trailer 142 | if data[strip3_end:] != "?"+msr.end_code: 143 | raise Exception("bad datablock : missing ? at position %d", strip3_end, data) 144 | 145 | return strip1, strip2, strip3 146 | 147 | @staticmethod 148 | def __encode_rawdatablock(strip1, strip2, strip3): 149 | # use empty string if you don't want to set a given strip : FIXME doesn't work 150 | datablock = "\x1bs" 151 | if strip1 != "": 152 | datablock += "\x1b\x01"+chr(len(strip1))+strip1 153 | if strip2 != "": 154 | datablock += "\x1b\x02"+chr(len(strip2))+strip2 155 | if strip3 != "": 156 | datablock += "\x1b\x03"+chr(len(strip3))+strip3 157 | datablock += "?\x1C" 158 | return datablock 159 | #return "\x1bs\x1b\x01"+chr(len(strip1))+strip1+"\x1b\x02"+chr(len(strip2))+strip2+"\x1b\x03"+chr(len(strip3))+strip3+"?\x1C" 160 | 161 | @staticmethod 162 | def pack_raw(data, mapping, bcount_code, bcount_output): 163 | # data : string to be encoded 164 | # mapping : string used to convert a character to a code 165 | # bcount_code : number of bits of character code (without the parity bit) 166 | # bcount_output : number of bits per output characters 167 | raw = "" 168 | lrc = 0 # parity odd 169 | rem_bits = 0 # remaining bits from previous loop 170 | rem_count = 0 # count of remaining bits 171 | for c in data: 172 | i = mapping.find(c) # convert char to code 173 | if i==-1: i = 0 # fail to first code if char is not allowed 174 | lrc ^= i 175 | i |= msr.parity_map[i] << bcount_code # add parity bit in front of the code 176 | rem_bits |= i << rem_count # concate current code in front of remaining bits 177 | rem_count += bcount_code+1 178 | if rem_count >= bcount_output: 179 | raw += chr(rem_bits & ((1<>= bcount_output 181 | rem_count -= bcount_output 182 | # add one loop for LRC 183 | lrc |= msr.parity_map[i] << bcount_code 184 | rem_bits |= lrc << rem_count 185 | rem_count += bcount_code+1 186 | if rem_count >= bcount_output: 187 | raw += chr(rem_bits & ((1<>= bcount_output 189 | rem_count -= bcount_output 190 | # add remaining bits, filling with 0 191 | if rem_count > 0: 192 | raw += chr(rem_bits) 193 | return raw 194 | 195 | @staticmethod 196 | def unpack_raw(raw, mapping, bcount_code, bcount_output): 197 | # raw : string to be encoded 198 | # mapping : string used to convert a character to a code 199 | # bcount_code : number of bits of character code (without the parity bit) 200 | # bcount_output : number of bits per output characters 201 | # returns : data without trailing nulls, total length including trailing nulls, parity errors, lrc error 202 | data = "" 203 | parity_errors = "" 204 | rem_bits = 0 # remaining bits from previous loop 205 | rem_count = 0 # count of remaining bits 206 | lrc = 0 # parity odd 207 | last_non_null = -1 208 | for c in raw: 209 | rem_count += bcount_output # append next bits on the right 210 | rem_bits = (rem_bits << bcount_output) | (ord(c) & ((1<= bcount_code+1: 212 | # get the bcount_code+parity bits on the left 213 | rem_count -= bcount_code+1 214 | i = rem_bits >> rem_count 215 | rem_bits &= ((1<>1] >> (6-bcount_code) 220 | data += mapping[i] 221 | if i != 0: last_non_null = len(data)-1 222 | 223 | # check parity 224 | lrc ^= i 225 | if msr.parity_map[i] == p: 226 | parity_errors += " " 227 | else: 228 | parity_errors += "^" 229 | 230 | # check LRC (kept at the end of decoded data) 231 | lrc_error = (lrc != 0) 232 | 233 | return data[0:last_non_null+1], len(data), parity_errors[0:last_non_null+1], lrc_error 234 | 235 | def read_tracks(self): 236 | status, _, data = self.__execute_waitresult("r") 237 | if status != "0": 238 | raise Exception("read error : %c" % status) 239 | return self.__decode_isodatablock(data) 240 | 241 | def read_raw_tracks(self): 242 | status, _, data = self.__execute_waitresult("m") 243 | if status != "0": 244 | raise Exception("read error : %c" % status) 245 | return self.__decode_rawdatablock(data) 246 | 247 | def write_tracks(self, t1="", t2="", t3=""): 248 | data = self.__encode_isodatablock(t1,t2,t3) 249 | status, _, _ = self.__execute_waitresult("w"+data) 250 | if status != "0": 251 | raise Exception("write error : %c" % status) 252 | 253 | def write_raw_tracks(self, t1, t2, t3): 254 | data = self.__encode_rawdatablock(t1,t2,t3) 255 | status, _, _ = self.__execute_waitresult("n"+data) 256 | if status != "0": 257 | raise Exception("write error : %c" % status) 258 | 259 | def erase_tracks(self, t1=False, t2=False, t3=False): 260 | mask = 0 261 | if t1: mask |= 1 262 | if t2: mask |= 2 263 | if t3: mask |= 4 264 | status, _, _ = self.__execute_waitresult("c"+chr(mask)) 265 | if status != "0": 266 | raise Exception("erase error : %c" % status) 267 | 268 | #def set_leadingzero(self, track13, track2): 269 | # status, result, _ = self.__execute_waitresult("o"+chr(bpc1)+chr(bpc2)+chr(bpc3)) 270 | # if status != "0": 271 | # raise Exception("set_bpc error : %c" % status) 272 | 273 | def set_bpc(self, bpc1, bpc2, bpc3): 274 | status, result, _ = self.__execute_waitresult("o"+chr(bpc1)+chr(bpc2)+chr(bpc3)) 275 | if status != "0": 276 | raise Exception("set_bpc error : %c" % status) 277 | 278 | def set_bpi(self, bpi1=None, bpi2=None, bpi3=None): 279 | modes = [] 280 | if bpi1==True: modes.append("\xA1") # 210bpi 281 | elif bpi1==False: modes.append("\xA0") # 75bpi 282 | if bpi2==True: modes.append("\xD2") 283 | elif bpi2==False: modes.append("\x4B") 284 | if bpi2==True: modes.append("\xC1") 285 | elif bpi2==False: modes.append("\xC0") 286 | for m in modes: 287 | status, result, _ = self.__execute_waitresult("b"+m) 288 | if status != "0": 289 | raise Exception("set_bpi error : %c for %s" % (status,hex(m))) 290 | 291 | def set_coercivity(self, hico): 292 | if hico: 293 | status, _, _ = self.__execute_waitresult("x") 294 | else: 295 | status, _, _ = self.__execute_waitresult("y") 296 | if status != "0": 297 | raise Exception("set_hico error : %c" % status) 298 | 299 | if __name__ == "__main__": 300 | # parse arguments 301 | import argparse 302 | parser = argparse.ArgumentParser() 303 | group = parser.add_mutually_exclusive_group(required=True) 304 | group.add_argument ('-r', '--read', action="store_true", help="read magnetic tracks") 305 | group.add_argument ('-w', '--write', action="store_true", help="write magnetic tracks") 306 | group.add_argument ('-e', '--erase', action="store_true", help="erase magnetic tracks") 307 | group.add_argument ('-C', '--hico', action="store_true", help="select high coercivity mode") 308 | group.add_argument ('-c', '--loco', action="store_true", help="select low coercivity mode") 309 | group.add_argument ('-b', '--bpi', help="bit per inch for each track (h or l)") 310 | parser.add_argument('-d', '--device', help="path to serial communication device") 311 | parser.add_argument('-0', '--raw', action="store_true", help="do not use ISO encoding/decoding") 312 | parser.add_argument('-t', '--tracks', default="123", help="select tracks (1, 2, 3, 12, 23, 13, 123)") 313 | parser.add_argument('-B', '--bpc', help="bit per caracters for each track (5 to 8)") 314 | parser.add_argument('data', nargs="*", help="(write only) 1, 2 or 3 arguments, matching --tracks") 315 | args = parser.parse_args(); 316 | 317 | if (args.read or args.erase) and len(args.data) != 0 or args.write and (len(args.data) != len(args.tracks)): 318 | print "too many arguments" 319 | parser.print_help() 320 | exit(1) 321 | 322 | tracks = [False, False, False] 323 | data = ["", "", ""] 324 | for i in range(0,len(args.tracks)): 325 | n = int(args.tracks[i])-1 326 | if(n<0 or n>2 or tracks[n]): 327 | parser.print_help() 328 | exit(1) 329 | tracks[n] = True 330 | if(args.write): 331 | data[n] = args.data[i] 332 | 333 | bpc1 = 8 334 | bpc2 = 8 335 | bpc3 = 8 336 | if args.bpc: 337 | bpc1 = ord(args.bpc[0])-48 338 | bpc2 = ord(args.bpc[1])-48 339 | bpc3 = ord(args.bpc[2])-48 340 | elif args.raw: 341 | args.bpc = "888" # force setup, as it's kept accross runs 342 | 343 | if args.bpi: 344 | bpi1 = args.bpi[0] != "l" 345 | bpi2 = args.bpi[1] != "l" 346 | bpi3 = args.bpi[2] != "l" 347 | 348 | # main code 349 | try: 350 | dev = msr(args.device) 351 | 352 | if args.bpc: 353 | dev.set_bpc(bpc1,bpc2,bpc3) 354 | 355 | if args.read & args.raw: 356 | s1,s2,s3 = dev.read_raw_tracks() 357 | def print_result(num, res): 358 | s,l,perr,lerr = res 359 | line = "%d=%s" % (num, s) 360 | if len(s) != l: line += " (+%d null)" % (l-len(s)) 361 | if lerr: line += " (LRC error)" 362 | print line 363 | if -1 != perr.find("^"): print " %s <- parity errors" % perr 364 | if tracks[0]: print_result(1, msr.unpack_raw(s1, msr.track1_map, 6, bpc1)) 365 | if tracks[1]: print_result(2, msr.unpack_raw(s2, msr.track23_map, 4, bpc2)) 366 | if tracks[2]: print_result(3, msr.unpack_raw(s3, msr.track23_map, 4, bpc3)) 367 | 368 | elif args.read: # iso mode 369 | s1,s2,s3 = dev.read_tracks() 370 | if tracks[0]: print "1=%s" % s1 371 | if tracks[1]: print "2=%s" % s2 372 | if tracks[2]: print "3=%s" % s3 373 | 374 | elif args.write & args.raw: 375 | d1 = "" 376 | d2 = "" 377 | d3 = "" 378 | if tracks[0]: 379 | d1 = msr.pack_raw(data[0], msr.track1_map, 6, bpc1) 380 | if tracks[1]: 381 | d2 = msr.pack_raw(data[1], msr.track23_map, 4, bpc2) 382 | if tracks[2]: 383 | d3 = msr.pack_raw(data[2], msr.track23_map, 4, bpc3) 384 | dev.write_raw_tracks(d1,d2,d3) 385 | 386 | elif args.write: # iso mode 387 | dev.write_tracks(data[0],data[1],data[2]) 388 | 389 | elif args.erase: 390 | dev.erase_tracks(tracks[0],tracks[1],tracks[2]) 391 | 392 | elif args.loco: 393 | dev.set_coercivity(msr.loco) 394 | 395 | elif args.hico: 396 | dev.set_coercivity(msr.hico) 397 | 398 | elif args.bpi: 399 | dev.set_bpi(bpi1,bpi2,bpi3) 400 | 401 | except Exception as e: 402 | print e 403 | -------------------------------------------------------------------------------- /msrtool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import sys 3 | import msr 4 | import tty 5 | import termios 6 | 7 | if len(sys.argv) < 2: 8 | print "USAGE: ./msrtool.py " 9 | sys.exit() 10 | 11 | dev = msr.msr(sys.argv[1]) 12 | 13 | 14 | def mode_read(dev): 15 | print "[r] swipe card to read, ^C to cancel" 16 | t1, t2, t3 = dev.read_tracks() 17 | print "Track 1:", t1 18 | print "Track 2:", t2 19 | print "Track 3:", t3 20 | 21 | 22 | def mode_compare(dev): 23 | print "[r] swipe card to read, ^C to cancel" 24 | t1, t2, t3 = dev.read_tracks() 25 | print "Track 1:", t1 26 | print "Track 2:", t2 27 | print "Track 3:", t3 28 | print "[r] swipe card to compare, ^C to cancel" 29 | b1, b2, b3 = dev.read_tracks() 30 | if b1 == t1 and b2 == t2 and t3 == t3: 31 | print "Compare OK" 32 | else: 33 | print "Track 1:", b1 34 | print "Track 2:", b2 35 | print "Track 3:", b3 36 | print "Compare FAILED" 37 | 38 | 39 | def bulk_compare(dev): 40 | print "[r] swipe card to read, ^C to cancel" 41 | t1, t2, t3 = dev.read_tracks() 42 | print "Track 1:", t1 43 | print "Track 2:", t2 44 | print "Track 3:", t3 45 | while True: 46 | print "[r] swipe card to compare, ^C to cancel" 47 | try: 48 | b1, b2, b3 = dev.read_tracks() 49 | except KeyboardInterrupt: 50 | break 51 | if b1 == t1 and b2 == t2 and t3 == t3: 52 | print "Compare OK" 53 | else: 54 | print "Track 1:", b1 55 | print "Track 2:", b2 56 | print "Track 3:", b3 57 | print "Compare FAILED" 58 | 59 | 60 | def mode_erase(dev): 61 | print "[e] swipe card to erase all tracks, ^C to cancel" 62 | dev.erase_tracks(t1=True, t2=True, t3=True) 63 | print "Erased." 64 | 65 | 66 | def mode_copy(dev): 67 | print "[c] swipe card to read, ^C to cancel" 68 | t1, t2, t3 = dev.read_tracks() 69 | print "Track 1:", t1 70 | print "Track 2:", t2 71 | print "Track 3:", t3 72 | kwargs = {} 73 | if t1 is not None: 74 | kwargs['t1'] = t1[1:-1] 75 | if t2 is not None: 76 | kwargs['t2'] = t2[1:-1] 77 | if t3 is not None: 78 | kwargs['t3'] = t3[1:-1] 79 | print "[c] swipe card to write, ^C to cancel" 80 | dev.write_tracks(**kwargs) 81 | print "Written." 82 | 83 | 84 | def bulk_copy(dev): 85 | print "[c] swipe card to read, ^C to cancel" 86 | t1, t2, t3 = dev.read_tracks() 87 | print "Track 1:", t1 88 | print "Track 2:", t2 89 | print "Track 3:", t3 90 | kwargs = {} 91 | if t1 is not None: 92 | kwargs['t1'] = t1[1:-1] 93 | if t2 is not None: 94 | kwargs['t2'] = t2[1:-1] 95 | if t3 is not None: 96 | kwargs['t3'] = t3[1:-1] 97 | while True: 98 | try: 99 | print "[c] swipe card to write, ^C to cancel" 100 | dev.write_tracks(**kwargs) 101 | print "Written." 102 | except KeyboardInterrupt: 103 | break 104 | except Exception as e: 105 | print "Failed. Error:", e 106 | break 107 | 108 | 109 | def mode_write(dev): 110 | print "[w] Input your data. Enter for not writing to a track." 111 | kwargs = {} 112 | print "Track 1:", 113 | t1 = raw_input().strip() 114 | print "Track 2:", 115 | t2 = raw_input().strip() 116 | print "Track 3:", 117 | t3 = raw_input().strip() 118 | if t1 != "": 119 | kwargs['t1'] = t1 120 | if t2 != "": 121 | kwargs['t2'] = t2 122 | if t3 != "": 123 | kwargs['t3'] = t3 124 | print "[w] swipe card to write, ^C to cancel" 125 | dev.write_tracks(**kwargs) 126 | print "Written." 127 | 128 | 129 | def bulk_write(dev): 130 | print "[w] Input your data. Enter for not writing to a track." 131 | kwargs = {} 132 | print "Track 1:", 133 | t1 = raw_input().strip() 134 | print "Track 2:", 135 | t2 = raw_input().strip() 136 | print "Track 3:", 137 | t3 = raw_input().strip() 138 | if t1 != "": 139 | kwargs['t1'] = t1 140 | if t2 != "": 141 | kwargs['t2'] = t2 142 | if t3 != "": 143 | kwargs['t3'] = t3 144 | while True: 145 | print "[w] swipe card to write, ^C to cancel" 146 | try: 147 | dev.write_tracks(**kwargs) 148 | except KeyboardInterrupt: 149 | break 150 | print "Written." 151 | 152 | 153 | def settings(dev): 154 | print """ 155 | Settings 156 | (h) hico (l) loco (q) quit 157 | """ 158 | while True: 159 | fd = sys.stdin.fileno() 160 | old_settings = termios.tcgetattr(fd) 161 | try: 162 | tty.setraw(fd) 163 | ch = sys.stdin.read(1) 164 | finally: 165 | termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 166 | if ch == 'q': 167 | break 168 | elif ch == 'h': 169 | dev.set_coercivity(dev.hico) 170 | print "Hico set." 171 | elif ch == 'l': 172 | dev.set_coercivity(dev.loco) 173 | print "Loco set." 174 | 175 | 176 | def quit(dev): 177 | print "[q] bye." 178 | sys.exit(0) 179 | 180 | 181 | while True: 182 | print """ 183 | What do you want to do? 184 | (r) read (w) write (c) copy 185 | (R) bulk read (W) bulk write (C) bulk copy 186 | (m) compare (e) erase (s) settings 187 | (M) bulk compare (E) bulk erase (q) quit 188 | """ 189 | fd = sys.stdin.fileno() 190 | old_settings = termios.tcgetattr(fd) 191 | try: 192 | tty.setraw(fd) 193 | ch = sys.stdin.read(1) 194 | finally: 195 | termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 196 | fnc = { 197 | 'r': mode_read, 198 | 'c': mode_copy, 199 | 'C': bulk_copy, 200 | 'e': mode_erase, 201 | 'w': mode_write, 202 | 'W': bulk_write, 203 | 'm': mode_compare, 204 | 'M': bulk_compare, 205 | 's': settings, 206 | 'q': quit, 207 | } 208 | if ch in fnc: 209 | try: 210 | fnc[ch](dev) 211 | except KeyboardInterrupt: 212 | continue 213 | except Exception as e: 214 | print "Failed. Error:", e 215 | continue 216 | elif ch.lower() in fnc: 217 | while True: 218 | try: 219 | fnc[ch.lower()](dev) 220 | except KeyboardInterrupt: 221 | break 222 | except Exception as e: 223 | print "Failed. Error:", e 224 | break 225 | else: 226 | print "Method not found." 227 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyserial 2 | --------------------------------------------------------------------------------