本示例使用的读卡器介绍:RS232串口RFID NFC IC卡读写器可二次开发编程发卡器USB转COM-淘宝网 (taobao.com)
#pip3 install pyserial 安装Python串口通讯库
import serial
import time
import serial.tools.list_ports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDateTime
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget,QMessageBox
from PyQt5.QtGui import QFont
from IC07UIS import Ui_Frame
import sys
import struct # struct的pack函数把任意数据类型变成字符串
import ctypes # 调用DLL动态库要有这个引用
selecom=0
ser = serial.Serial()
ComIsOpen=False
OrderCode=0
RcvBuff=b''
class SerialListenThread(QThread): # 串口侦听线程
serial_data = pyqtSignal(int,str) # 创建一个信号,触发时传递显示信息给槽函数
def run(self):
global ComIsOpen
global OrderCode
global RcvBuff
while ComIsOpen:
try:
rcvdata = ser.read_all()
if (len(rcvdata) > 0):
if (OrderCode<=1):
GetData = '接收数据:'
for num in range(0, len(rcvdata)):
GetData = GetData + '%02X ' % (rcvdata[num])
self.serial_data.emit(1, GetData)
else:
RcvBuff = RcvBuff + rcvdata
if (len(RcvBuff)>=RcvBuff[0]+2):
deldata=RcvBuff[0:RcvBuff[0]+2]
GetData = '接收数据:'
for num in range(0, len(deldata)):
GetData = GetData + '%02X ' % (deldata[num])
self.serial_data.emit(1, GetData)
crc = 0 # 计算返回数据的校验位
for num in range(1, len(deldata) - 1):
crc = crc ^ deldata[num]
if (crc == deldata[len(deldata) - 1]):
if(OrderCode==2): #寻卡指令返回信息
if (deldata[0] == 1 and deldata[1] == 8):
self.serial_data.emit(2, '未寻到卡!')
elif (deldata[0] == 1 and deldata[1] == 9):
self.serial_data.emit(2, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[0] == 1 and deldata[1] == 10):
self.serial_data.emit(2, '无法选择激活卡片!')
elif (deldata[0] == 5):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]))
elif(OrderCode==3): #轻松读IC卡扇区指令返回数据
if (deldata[0] == 1 and deldata[1] == 8):
self.serial_data.emit(2, '未寻到卡!')
elif (deldata[0] == 1 and deldata[1] == 9):
self.serial_data.emit(2, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[0] == 5):
if (deldata[1] == 1):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',密码认证成功,但读取扇区内容失败!')
elif (deldata[1] == 2):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',第0块读出,但第1、2块没读出,仅扇区内容前16个字节的数据有效!')
elif (deldata[1] == 3):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',第0、1块读出,但第2块没读出,仅扇区内容前32个字节的数据有效!')
elif (deldata[1] == 10):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',无法选择激活卡片!')
elif (deldata[1] == 12):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',密码认证失败!')
else:
self.serial_data.emit(2, '操作失败,错误代码:' + str(deldata[1]))
elif (deldata[0] == 53):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',读取扇区数据成功!')
ReturnInf = ''
for num in range(6, len(deldata) - 1):
ReturnInf = ReturnInf + '%02X ' % (deldata[num])
self.serial_data.emit(3,ReturnInf)
elif (OrderCode == 4):
if (deldata[0] == 1 and deldata[1] == 8):
self.serial_data.emit(2, '未寻到卡!')
elif (deldata[0] == 1 and deldata[1] == 9):
self.serial_data.emit(2, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[0] == 5):
if (deldata[1] == 0):
self.serial_data.emit(4, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',写扇区数据成功!')
elif (deldata[1] == 1):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',密码认证成功,但写扇区内容失败!')
elif (deldata[1] == 2):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',第0块写入成功,但第1、2块写入失败!')
elif (deldata[1] == 3):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',第0、1块写入成功,但第2块写入失败!')
elif (deldata[1] == 10):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',无法选择激活卡片!')
elif (deldata[1] == 12):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',密码认证失败!')
else:
self.serial_data.emit(2, '操作失败,错误代码:' + str(deldata[1]))
elif (OrderCode == 5):
if (deldata[0] == 1 and deldata[1] == 8):
self.serial_data.emit(2, '未寻到卡!')
elif (deldata[0] == 1 and deldata[1] == 9):
self.serial_data.emit(2, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[0] == 5):
if (deldata[1] == 0):
self.serial_data.emit(4, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',修改IC卡密码成功!')
elif (deldata[1] == 10):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',无法选择激活卡片!')
elif (deldata[1] == 12):
self.serial_data.emit(2, '寻到IC卡,16进制卡号:%02X %02X %02X %02X ' % (deldata[2], deldata[3], deldata[4], deldata[5]) + ',密码认证失败!')
else:
self.serial_data.emit(2, '操作失败,错误代码:' + str(deldata[1]))
elif (OrderCode == 6): # 写Ntag卡返回状态
if (deldata[1] == 0):
cardnohex='%02X%02X%02X%02X%02X%02X%02X' %(deldata[2], deldata[3], deldata[4], deldata[5], deldata[6], deldata[7], deldata[8])
self.serial_data.emit(6, '卡号:' + cardnohex+',写卡成功!')
elif (deldata[1] == 8):
self.serial_data.emit(6, '未寻到卡!')
elif (deldata[1] == 9):
self.serial_data.emit(6, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[1] == 12):
self.serial_data.emit(6, '卡号:' + cardnohex+',密码认证失败!')
elif (deldata[1] == 14):
self.serial_data.emit(6, '卡号:' + cardnohex+',写卡失败,可能要带密码操作!')
else:
self.serial_data.emit(6, '写卡错误,返回代码:%d' %(deldata[1]))
elif (OrderCode == 7): # 读Ntag卡返回状态
if (deldata[1] == 0):
cardnohex='%02X%02X%02X%02X%02X%02X%02X' %(deldata[2], deldata[3], deldata[4], deldata[5], deldata[6], deldata[7], deldata[8])
self.serial_data.emit(6, '卡号:' + cardnohex+',读卡成功!')
GetdataStr=''
for num in range(11 , len(deldata)-1 ):
GetdataStr=GetdataStr+ '%02X '%(deldata[num])
self.serial_data.emit(7, GetdataStr)
elif (deldata[1] == 8):
self.serial_data.emit(6, '未寻到卡!')
elif (deldata[1] == 9):
self.serial_data.emit(6, '两张以上卡片同时在感应区,发生冲突!')
elif (deldata[1] == 12):
self.serial_data.emit(6, '卡号:' + cardnohex+',密码认证失败!')
elif (deldata[1] == 13):
self.serial_data.emit(6, '卡号:' + cardnohex+',读卡失败,可能要带密码操作!')
else:
self.serial_data.emit(6, '写卡错误,返回代码:%d' %(deldata[1]))
RcvBuff = RcvBuff[RcvBuff[0] + 2:]
if(len(RcvBuff)==0):
OrderCode=0
except:
self.serial_data.emit(9, '串口通讯异常,请重新打开与设备相连的串口继续......')
class MainWindow(QtWidgets.QMainWindow, Ui_Frame):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.Butt_Open.clicked.connect(self.Butt_Open_click)
self.Butt_Beep.clicked.connect(self.Butt_Beep_click)
self.Butt_Request.clicked.connect(self.Butt_Request_click)
self.Com_Serial.currentIndexChanged.connect(self.Com_Serialselectionchange)
self.Butt_ReadCard.clicked.connect(self.Butt_ReadCard_click)
self.Butt_Writecard.clicked.connect(self.Butt_Writecard_click)
self.Butt_Changekey.clicked.connect(self.Butt_Changekey_click)
self.Butt_Sendhex.clicked.connect(self.Butt_Sendhex_click)
self.Butt_Clear.clicked.connect(self.Butt_Clear_click)
self.checkBox_authen.toggled.connect(self.checkBox_authen_toggled)
self.PB_init.clicked.connect(self.PB_init_clicked)
self.PB_easyread.clicked.connect(self.PB_easyread_clicked)
self.PB_easywrite.clicked.connect(self.PB_easywrite_clicked)
def SerialGetData(self,dispcode,Getdata):
global ComIsOpen
global ser
if (dispcode == 1):
self.ListAddItem(Getdata)
elif (dispcode == 2):
self.Label_Cardno.setText(Getdata)
elif (dispcode == 3):
self.Text_Data.setText(Getdata)
self.Butt_Beep_click()
elif (dispcode == 4):
self.Label_Cardno.setText(Getdata)
self.Butt_Beep_click()
elif (dispcode == 6):
self.Label_status.setText(Getdata)
elif (dispcode == 7):
self.Text_TagData.setPlainText(Getdata)
elif (dispcode == 9):
self.ListAddItem(Getdata)
ser = serial.Serial()
ser.close()
ComIsOpen = False
self.Butt_Open.setText('打开串口')
self.Com_Serial.clear() # 刷新串口列表,看是否有新串口
port_list = list(serial.tools.list_ports.comports())
for i in range(0, len(port_list)):
self.Com_Serial.addItem(list(port_list[i])[0])
if (self.Com_Serial.count() - 1 >= selecom):
self.Com_Serial.setCurrentIndex(selecom)
else:
self.Com_Serial.setCurrentIndex(0)
def checkBox_authen_toggled(self):
if self.checkBox_authen.isChecked():
self.lineEdit_authkey.setVisible(True)
else:
self.lineEdit_authkey.setVisible(False)
def Butt_Clear_click(self):
self.List_Info.clear()
def ListAddItem(self,itemstr):
if self.List_Info.count() > 10:
self.List_Info.clear()
self.List_Info.addItem(itemstr)
self.List_Info.scrollToBottom()
seleid = self.List_Info.count() - 1
self.List_Info.item(seleid).setSelected(True)
def Com_Serialselectionchange(self):
global ComIsOpen
global ser
ser = serial.Serial()
ser.close()
ComIsOpen = False
self.Butt_Open.setText('打开串口')
def Butt_Open_click(self):
global ComIsOpen
global ser
global selecom
global RcvBuff
if (self.Butt_Open.text() == '打开串口'):
port_serial = self.Com_Serial.currentText()
port_baud = int(self.Com_Baud.currentText())
port_bytesize = int(self.Com_Size.currentText())
port_parity = self.Com_Parity.currentText()
port_stopbits = int(self.Com_Stop.currentText())
try:
ser = serial.Serial(port_serial, port_baud, bytesize=port_bytesize, parity=port_parity,
stopbits=port_stopbits, xonxoff=False, rtscts=False, dsrdtr=False)
if (ser.isOpen()):
selecom = self.Com_Serial.currentIndex()
self.subSerialListenThread = SerialListenThread()
self.subSerialListenThread.serial_data.connect(self.SerialGetData)
self.subSerialListenThread.start() # 开启串口接收线程
RcvBuff = b''
ComIsOpen = True
self.Butt_Open.setText('关闭串口')
except:
res = QMessageBox.critical(self, "警告", " 您选择的串口打开失败,请检查该串口是否已被其他应用占用!", QMessageBox.Yes)
self.Com_Serial.clear() # 刷新串口列表,看是否有新串口
port_list = list(serial.tools.list_ports.comports())
for i in range(0, len(port_list)):
self.Com_Serial.addItem(list(port_list[i])[0])
if (self.Com_Serial.count() - 1 >= selecom):
self.Com_Serial.setCurrentIndex(selecom)
else:
self.Com_Serial.setCurrentIndex(0)
else:
ser = serial.Serial()
ser.close()
ComIsOpen = False
self.Butt_Open.setText('打开串口')
self.Com_Serial.clear() # 刷新串口列表,看是否有新串口
port_list = list(serial.tools.list_ports.comports())
for i in range(0, len(port_list)):
self.Com_Serial.addItem(list(port_list[i])[0])
if (self.Com_Serial.count() - 1 >= selecom):
self.Com_Serial.setCurrentIndex(selecom)
else:
self.Com_Serial.setCurrentIndex(0)
def Butt_Beep_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
OrderCode=1
beepdelay = self.Sp_Delay.value() # 响声时长,单位豪秒
sdata = bytes([eval('0x03')]) # 1字节指令长度
sdata = sdata + bytes([eval('0x0F')]) # 1字节功能码
sdata = sdata + bytes([beepdelay % 256]) # 2字节蜂鸣响声延时 低位
sdata = sdata + bytes([beepdelay // 256]) # 高位
sdata = sdata + bytes([sdata[1] ^ sdata[2] ^ sdata[3]]) # 1字节异或和校验码
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf=''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:'+SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def Butt_Request_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
self.Label_Cardno.setText('')
OrderCode=2
sdata = bytes([eval('0x01'), eval('0xF0'), eval('0xF0')])
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf=''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:'+SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def Butt_ReadCard_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
try:
keys = bytes.fromhex(self.Text_key.toPlainText())
except:
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
if(len(keys)!=6):
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
sdata = bytes([eval('0x0E'), eval('0x78'), eval('0x17')]) # 指令长度 、功能码、读写卡操作权限
sdata = sdata + bytes.fromhex('00000000') # 4字节的16进制 要操作的卡号,如果全部取0表示可以 操作任意卡
sdata = sdata + bytes([self.Com_Area.currentIndex()]) # 操作扇区号,取值0-15
sdata = sdata + bytes([self.Com_Auth.currentIndex()]) # 卡片认证方式,1为A密码认证,0为B密码认证,推荐取1
sdata = sdata + keys # 6字节的16进制 卡片认证密码
crc = 0 # 计算校验位
for num in range(1, 15):
crc = crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode=3
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def Butt_Writecard_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
try:
keys = bytes.fromhex(self.Text_key.toPlainText())
except:
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
if(len(keys)!=6):
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
try:
wdata = bytes.fromhex(self.Text_Data.toPlainText())
except:
QMessageBox.critical(self, "提示", "写卡数据输入错误!", QMessageBox.Yes)
return
if(len(wdata)<48):
res = QMessageBox.question(self, "提示", "写卡数据不足48位,是否要补足48位写入?",QMessageBox.Yes | QMessageBox.No)
if (QMessageBox.No == res):
return
else:
for num in range(0, 48):
wdata=wdata+bytes([0])
wdata=wdata[0:48]
sdata = bytes([eval('0x3E'), eval('0x69'), eval('0x17')]) # 指令长度 、功能码、读写卡操作权限
sdata = sdata + bytes.fromhex('00000000') # 4字节的16进制 要操作的卡号,如果全部取0表示可以 操作任意卡
sdata = sdata + bytes([self.Com_Area.currentIndex()]) # 操作扇区号,取值0-15
sdata = sdata + bytes([self.Com_Auth.currentIndex()]) # 卡片认证方式,1为A密码认证,0为B密码认证,推荐取1
sdata = sdata + keys # 6字节的16进制 卡片认证密码
sdata = sdata + wdata # 48个字节的写入数据
crc = 0 # 计算校验位
for num in range(1, 63):
crc = crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode=4
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def Butt_Changekey_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
try:
keys = bytes.fromhex(self.Text_key.toPlainText())
except:
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
if(len(keys)!=6):
QMessageBox.critical(self, "提示", "卡密码输入错误!", QMessageBox.Yes)
return
try:
newkey = bytes.fromhex(self.Text_Newkey.toPlainText())
except:
QMessageBox.critical(self, "提示", "新密码及控制位输入错误!", QMessageBox.Yes)
return
if(len(newkey)!=16):
QMessageBox.critical(self, "提示", "新密码及控制位输入错误,请输入6字节新A密码+4字节控制位+6字节新B密码!", QMessageBox.Yes)
return
res = QMessageBox.question(self, "警告", " 您确定要继续执行修改卡密码及控制位的操作吗?如确定修改请务必记住卡的新密码,否则卡片将报废!",QMessageBox.Yes | QMessageBox.No)
if (QMessageBox.Yes == res):
sdata = bytes([eval('0x1F'), eval('0xF1'), eval('0x17')]) # 指令长度 、功能码、读写卡操作权限
sdata = sdata + bytes.fromhex('00000000') # 4字节的16进制 要操作的卡号,如果全部取0表示可以 操作任意卡
sdata = sdata + bytes([self.Com_Area.currentIndex()]) # 操作扇区号,取值0-15
sdata = sdata + bytes([self.Com_Auth.currentIndex()]) # 卡片认证方式,1为A密码认证,0为B密码认证,推荐取1
sdata = sdata + keys # 6字节的16进制 卡片认证密码
sdata = sdata + newkey # 16个字节的新密码
sdata = sdata + bytes([3]) # 取值0只修改A密码,取值2修改AB密码,取值3修改AB密码及控制位
crc = 0 # 计算校验位
for num in range(1, 32):
crc = crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode=5
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def Butt_Sendhex_click(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
if(ComIsOpen):
self.Label_Cardno.setText('')
try:
sdata = bytes.fromhex(self.Text_HexData.toPlainText())
except:
QMessageBox.critical(self, "提示", "发送数据输入错误!", QMessageBox.Yes)
return
if(len(sdata)==0):
QMessageBox.critical(self, "提示", "请输入要向串口发送的十六进制数据!", QMessageBox.Yes)
return
OrderCode=0
RcvBuff=b''
ser.write(sdata) # 向串口写入数据
SendInf=''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:'+SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def PB_init_clicked(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
self.Label_status.setText('')
if (ComIsOpen):
myctrlword = 0
if self.checkBox_authen.isChecked(): #需要先认证卡片密钥
oldkeystr=self.lineEdit_authkey.text().strip()
if is_valid_hex(oldkeystr)==False or len(bytes.fromhex(oldkeystr))!=4 :
QMessageBox.critical(self, "提示", "认证密钥输入错误,请输入8位16进制认证密钥!", QMessageBox.Yes)
self.lineEdit_authkey.setFocus()
return
myctrlword=16
else:
oldkeystr ='00000000'
if self.Com_keyen.currentIndex() ==0: #开启卡密钥保护
newkeystr=self.lineEdit_newkey.text().strip()
if is_valid_hex(newkeystr)==False or len(bytes.fromhex(newkeystr))!=4 :
QMessageBox.critical(self, "提示", "新密钥输入错误,请输入8位16进制密钥!", QMessageBox.Yes)
self.lineEdit_newkey.setFocus()
return
errnum=self.Sp_errnum.value() % 8 #启用密码认证次数
if self.Com_RWKeyOn.currentIndex() ==1: #读卡也要认证密码
errnum=errnum+128
myctrlword = myctrlword + 7
configdata = bytes.fromhex('000000')
configdata=configdata+bytes([self.Sp_initbeg.value()]) #起始保护页
configdata = configdata + bytes([errnum])
configdata = configdata + bytes.fromhex('000000')
configdata = configdata + bytes.fromhex(newkeystr)
configdata = configdata + bytes.fromhex('16160000')
else: #关闭卡密钥保护
myctrlword = myctrlword + 3
configdata=bytes.fromhex('000000FF000000000000000000000000')
sdata = bytes.fromhex('1D16') #指令长度、功能码
sdata = sdata + bytes([myctrlword])
sdata = sdata + bytes.fromhex('00000000000000') #7字节要操作的卡片UID,全取0表示操作发卡器上任意卡
sdata = sdata + bytes.fromhex(oldkeystr) #4字节卡认证密钥
sdata = sdata + configdata
crc=0
for num in range(1,len(sdata)):
crc=crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode = 6
RcvBuff = b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def PB_easyread_clicked(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
self.Label_status.setText('')
self.Text_TagData.setPlainText('')
if (ComIsOpen):
myctrlword = 0
if self.checkBox_authen.isChecked(): # 需要先认证卡片密钥
oldkeystr = self.lineEdit_authkey.text().strip()
if is_valid_hex(oldkeystr) == False or len(bytes.fromhex(oldkeystr)) != 4:
QMessageBox.critical(self, "提示", "认证密钥输入错误,请输入8位16进制认证密钥!", QMessageBox.Yes)
self.lineEdit_authkey.setFocus()
return
myctrlword = 16
else:
oldkeystr = '00000000'
if self.Sp_RWNum.value()>12:
QMessageBox.critical(self, "提示", "每次读取页数取值范围 0~12!", QMessageBox.Yes)
return
sdata = bytes.fromhex('0F1B') # 指令长度、功能码
sdata = sdata + bytes([myctrlword]) # 控制字 0 表示无需密码操作任意卡; +8 表示指定卡;+16表示 带密码操作
sdata = sdata + bytes.fromhex('00000000000000') # 7字节要操作的卡片UID,全取0表示操作发卡器上任意卡
sdata = sdata + bytes.fromhex(oldkeystr) # 4字节卡认证密钥
sdata = sdata + bytes([self.Sp_RWBegin.value()]) # 读起始页
sdata = sdata + bytes([self.Sp_RWNum.value()]) # 读取页数
crc = 0
for num in range(1, len(sdata)):
crc = crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode = 7
RcvBuff = b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def PB_easywrite_clicked(self):
global ComIsOpen
global ser
global OrderCode
global RcvBuff
self.Label_status.setText('')
if (ComIsOpen):
myctrlword = 0
if self.checkBox_authen.isChecked(): # 需要先认证卡片密钥
oldkeystr = self.lineEdit_authkey.text().strip()
if is_valid_hex(oldkeystr) == False or len(bytes.fromhex(oldkeystr)) != 4:
QMessageBox.critical(self, "提示", "认证密钥输入错误,请输入8位16进制认证密钥!", QMessageBox.Yes)
self.lineEdit_authkey.setFocus()
return
myctrlword = 16
else:
oldkeystr = '00000000'
if self.Sp_RWNum.value()==0 or self.Sp_RWNum.value()>11:
QMessageBox.critical(self, "提示", "每次写页数取值范围 1~11!", QMessageBox.Yes)
return
WriteLen = self.Sp_RWNum.value()*4
writedatastr=self.Text_TagData.toPlainText().strip()
if is_valid_hex(writedatastr) == False or len(bytes.fromhex(writedatastr)) < WriteLen:
QMessageBox.critical(self, "提示", "写入数据错误,请输入 %d" %(WriteLen*2) +" 位16进制的写入数据!", QMessageBox.Yes)
return
writedatabuf=bytes.fromhex(writedatastr)[:WriteLen]
sdata = bytes([15+WriteLen]) # 指令长度
sdata = sdata + bytes([eval('0x1C')]) # 功能码
sdata = sdata + bytes([myctrlword]) # 控制字 0 表示无需密码操作任意卡; +8 表示指定卡;+16表示 带密码操作
sdata = sdata + bytes.fromhex('00000000000000') # 7字节要操作的卡片UID,全取0表示操作发卡器上任意卡
sdata = sdata + bytes.fromhex(oldkeystr) # 4字节卡认证密钥
sdata = sdata + bytes([self.Sp_RWBegin.value()]) # 读起始页
sdata = sdata + bytes([self.Sp_RWNum.value()]) # 读取页数
sdata = sdata + writedatabuf
crc = 0
for num in range(1, len(sdata)):
crc = crc ^ sdata[num]
sdata = sdata + bytes([crc])
OrderCode = 6
RcvBuff = b''
ser.write(sdata) # 向串口写入数据
SendInf = ''
for num in range(0, len(sdata)):
SendInf = SendInf + '%02X ' % (sdata[num])
self.Text_HexData.setText(SendInf)
self.ListAddItem('发送数据:' + SendInf)
else:
QMessageBox.critical(self, "提示", "请先选择打开与读写器相连的串口!", QMessageBox.Yes)
def is_valid_hex(input_str):
input_str = input_str.replace(" ", "")
try:
if (len(bytes.fromhex(input_str)) == len(input_str) / 2):
return True
else:
return False
except:
return False
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
screen = QDesktopWidget().screenGeometry()
size = mainWindow.geometry()
# 获得窗口相关坐标
newLeft = (screen.width() - size.width()) // 2
newTop = (screen.height() - size.height()) // 2
# 移动窗口使其居中
mainWindow.move(newLeft, newTop)
mainWindow.setWindowTitle("IC-07串口读写器 Python Demo")
mainWindow.show()
port_list = list(serial.tools.list_ports.comports())
for i in range(0, len(port_list)):
mainWindow.Com_Serial.addItem(list(port_list[i])[0])
mainWindow.Com_Baud.setCurrentIndex(2)
mainWindow.Com_Size.setCurrentIndex(3)
mainWindow.Com_Stop.setCurrentIndex(0)
mainWindow.Sp_Delay.setMaximum(65535)
mainWindow.Sp_Delay.setValue(50)
mainWindow.Com_Auth.setCurrentIndex(1)
mainWindow.lineEdit_authkey.setVisible(False)
mainWindow.Com_keyen.setCurrentIndex(1)
mainWindow.Label_Cardno.setStyleSheet("color: red;")
mainWindow.label_8.setStyleSheet('color:blue;')
mainWindow.label_14.setStyleSheet('color:blue;')
mainWindow.Label_status.setStyleSheet("color: red;")
sys.exit(app.exec_())
源码下载:Python串口读写器windows、linux系统源码资源-CSDN文库