江苏大学 物联网专业课程设计
23/06/27 鉴于这两天有读者私信的:
①打开界面接收不到数据的问题
②.Python3.8版本plt.show导致主线程阻塞,绘图失败的问题
以上问题我于2023/06/27重新测试并添加必要修改后程序可正常运行,需要帮忙解决上述类似问题的读者私信我有偿指导修改。附上我于今早测试的效果图
最终实现代码仍有很多BUG未解决,但能实现课设要求的基本功能,此文章仅作参考。
一.前期准备
老师一般会给布三个必备的工具/软件:分别为【RCPET】【sscom32】和【VSPD 6.9】
1.1 RCPET
该软件是老师已写好的“快递柜模拟程序”。之后所写的Python程序主要就是与【RCPET】进行数据的交互。解压【RCPET.rar】点击【RCP_ET.exe】即可运行。
1.2 sscom32
串口调试助手,前期可用于测试【模拟串口】是否能正常通信,解压点击【sscom5.13.1.exe】即可使用
1.3 VPSD
虚拟串口工具,用于在PC上创建虚拟串口对,若下载未破解版的VPSD,注意其有试用期
二.关键代码设计
注;该部分只给出关键代码的功能解释,可参照源码进行理解。
2.1【crc16_xmodem+get_crc_value】 CRC校验
CRC校验码的生成与校对 【crc16_xmodem+get_crc_value】 此次串口通信收发的数据帧均要求实现CRC校验,只有CRC校验合法的数据帧才能进行进一步的分析处理。CRC校验函数的依据CRC中的“xmodem”规则编写,每次将数据帧中去除“信息头”、“校验”和“结束标志”的部分传入进行校验计算,计算结果与数据帧携带的“校验”部分相比较,若二者一致则表示该数据包CRC校验合法。
2.2【build_frame(length,function,data)】数据帧的构建
该函数用于实现构建数据帧的功能。三个参数分别为:“length”为需构建数据帧的长度,需以16进制传入、“function”为需构建数据帧的功能号、“data”为需构建数据包的真正数据部分。
2.3 【recv_data()】数据帧实时接收
该函数用于实现控制端实时接收快递柜端发送的各类帧。函数先将串口接收的数据进行CRC的合法性校验,校验通过后根据“上次状态帧”和“Ack帧”两种不同的帧类型执行不同的操作。同时根据接收的合法数据帧,对界面中的“.温度参数”、“抽屉状态”和“压缩机状态监”进行状态更新。
2.4【openBox()】 抽屉锁控
函数主要实现控制端通过串口通信模拟远程控制抽屉的开关。对抽屉的控制具体转化为构建12Byte的“开锁帧”。锁控的开关通过button点击事件模拟,按下置1表示开,否则置0表示关。通过规定的编码方式[抽屉8+抽屉7+抽屉6+抽屉5+抽屉4+抽屉3+抽屉2+”000000”+抽屉10+抽屉9]将抽屉状态抽象为16位2进制数,进而转换为2B的16进制数据部分。最后调用build_lock_frame()函数将构建好的“开锁帧”发出,实现开关锁的控制。
2.5【paramControl()】设置设备参数
该函数主要实现对快递柜各项参数的修改。修改项包括:“设备编码”、“设备地址”、“上传间隔”、“压缩机启动延时”、“设定温度”和“温度控制偏差”。通过读取子窗口各entry中数据后调用build_data_frame()函数构建好“设置参数帧”并发出,实现设备参数的设置。
2.6 【tempControl()】设置设定温度
该函数主要实现设置快递柜的设定温度。通过读取子界面entry中的数值后调用build_temp_frame()函数构建好“设置温度帧”并发出,实现设定温度的设置。
2.7【pressorControl()】压缩机自动控制
该函数主要实现压缩机的开启和自动控制。点击“压缩机控制”,压缩机的总开关被打开,依据课设指导书中的要求,压缩机打开后将实现温度自动控制。压缩机会将抽屉中的“采集温度”控制在“设定温度”±“控制偏差”的范围内。
2.8【pressorControl2()】压缩机关闭
该函数主要实现关闭压缩机的总开关。点击“压缩机关闭”,无论此时压缩机处于何种状态,都将其关闭。只有重新点击“压缩机控制”按钮压缩机才会正常工作。
2.9【draw_temp()】温度曲线绘制
该函数主要实现将串口实时读取的快递柜采集温度绘制成温度曲线,以便直观地观察快递柜的温度变化。主要通过Python的matplotlib.pyplot包中的散点图scatter实现。通过调整draw_temp()中两个for循环的参数可以调节每次绘制散点图的最大数量。
三.运行效果
3.1 数据帧接收
3.2 设置设参数
①点击“设置设备参数”;
②在“设置”窗口中输入需设置的数值后再点击“发送”;
③可在快递柜端“系统参数”一栏看到设备参数已修改成功。
3.3 抽屉的开关
初始化时抽屉状态:
进行开关抽屉操作:
①.点击“打开抽屉”按钮;
②.在子窗口“抽屉控制开关”中勾选想要打开的抽屉,并点击发送;
③.快递柜端抽屉状态随②中勾选的选项进行改变;
④.终端根据快递柜回送的Ack帧更新抽屉面板状态
3.4 设置设定温度
①.点击“设置温度”按钮;
②.在“温度控制”子窗口输入想要设置的温度,点击“发送”按钮;
③.可观察到终端【设定温度】一栏已被设置为②中输入的温度。
3.5 压缩机自动控制
3.6 绘制温度曲线
①.点击“压缩机控制”按钮,进行压缩机自动控制,以便观察温度曲线;
②.点击“绘制温度曲线”按钮,开始绘制实时温度曲线;
③.等待片刻,可观察到如图所示的温度曲线,温度在下降到“设定温度”的偏差范围内压缩机开始介入,通过自动控制,将温度控制在一个合理的范围区间内。
四.BUG说明及源代码
4.1 BUG说明
①用于发送开关压缩机状态帧(源码535行-546行)写在了recv_data中,若在开关压缩机的同时再点击【打开抽屉】进行抽屉开关控制,可能会导致报错。
②绘制实时温度曲线的函数【draw_temp】中每次绘图的散点个数的固定的【由两组for循环中的数字决定】,若在【固定】散点个数未绘制结束之前关闭主窗口,会导致无限弹窗,目前尚未解决。
③ 由于【RCPET】返回帧的速度较快,有时点击开关抽屉会接收不到返回的Ack帧,Ack帧可能会与其他帧连在一起导致无法接收,也没有很好的解决方法。在点击开关抽屉时可适当慢一点
④ 源码中有很多地方为了方便使用了大量的重复if代码,可自行优化。
该源码仍有很多的BUG和待完善的地方,仅作为参考交流。
4.2 程序源码
编写不易,点个赞吧
from tkinter import ttk, messagebox
from tkinter import *
import tkinter as tk
import time
import binascii
import serial.tools.list_ports
from time import sleep
import threading
import matplotlib.pyplot as plt
from binascii import unhexlify
from crcmod import mkCrcFun #用于CRC校验
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
frame_num=0 #全局帧号
ser=serial.Serial(
port="COM2",
baudrate=38400,
bytesize=8,
parity=serial.PARITY_NONE,
stopbits=1,
timeout=0.2)
box0=0
box1=0
box2=0
box3=0
box4=0
box5=0
box6=0
box7=0
box8=0
box9=0
"绘制曲线图的温度数据"
draw=0
box="0" #2B的锁控位 16进制
box_recv="0" #2B锁控返回位 16进制
press_recv="0" #压缩机工作状态
device_code="FFFFFFFFFF" #设备编码 默认全F
device_add="01" #设备地址 缺省为1
post_time ="01" #上传间隔 缺省为1
pressor_start_delay= "1e" #压缩机启动延时 缺省30-》1e
Temp="08" #当前温度的16进制字符串 缺省为4 转为8
Temp_pc = "02" #温控偏差 缺省为2
recv="0"
Temp_recv=0 #返回的设定温度
Temp_pc_recv=0 #返回的温度控制偏差
press_button=0 #压缩机总开关 0是关 1是开
def draw_temp(): # 绘制温度曲线图
global draw #全局温度变化量
i = 0
t = []
list1 = []
plt.figure(1)
plt.xlabel("时间(s)")
plt.ylabel("温度(℃)")
plt.title("温度实时监控曲线")
for m in range(10): #两个for循环控制每次画图的总点数
for x in range(5):
list1.append(draw)
i = i + 1
t.append(i)
plt.scatter(i, draw) #绘图点坐标 温度用全局变量
plt.pause(1.5)
plt.plot(t, list1) #连线
plt.plot(t, list1) #总连线
plt.grid()
plt.show()
time.sleep(2)
plt.cla() #清除原图
def xitong():
global frame_num
root = Tk()
root.title('智能快递柜终端v1.0')
root.geometry('800x550')
root.maxsize(800, 800)
root.minsize(800, 550)
##################工作函数区###################
def left_zero_hex1(data): # 1B 16进制左补0
b = data
if (len(data) == 1): b = "0" + data # 小于两位 补0
return b
def left_zero_hex2(data): # 2B 16进制左补0
b = "0000"
if (len(data) == 1): b = "000" + data
if (len(data) == 2): b = "00" + data
if (len(data) == 3): b = "0" + data
if (len(data) == 4): b = data
return b
def frame_num_re(): # 帧号合法性验证
global frame_num
if frame_num > 255:
frame_num = 0
def get_crc_value(s, crc16): #crc工作函数
data = s.replace(' ', '')
crc_out = hex(crc16(unhexlify(data)))
str_list = list(crc_out)
if len(str_list) == 5:
str_list.insert(2, '0') # 位数不足补0
crc_data = ''.join(str_list[2:])
return crc_data[2:] + crc_data[:2]
def crc16_xmodem(s): #xmodem工作函数
crc16 = mkCrcFun(0x11021, rev=False, initCrc=0x0000, xorOut=0x0000)
return get_crc_value(s, crc16)
def build_frame(length, function, data): # 帧长,功能,数据
global frame_num
frame_num=frame_num+1
frame_num_re()
f = []
f.append("ffff")
f.append(length) # 帧长
f.append(left_zero_hex1(hex(frame_num)[2:])) # 帧号10转16
f.append(device_add)
f.append(function) # 功能号
f.append(data) # 数据载荷
############CRC#################
f_crc = ''.join(f)
f_crc=crc16_xmodem(f_crc[4:]) #计算生成crc
f.append(f_crc) # CRC
############CRC#################
f.append("fff7") # 帧结尾标识
f_return = ''.join(f)
return f_return
##################工作函数区###################
##################控制函数区###################
def paramControl(): #设备参数控制
def build_data_frame(): # 整合设备参数数据部分
global frame_num
devide_code = entry1.get() # 长度5B
print("设备编码", devide_code)
devide_add = left_zero_hex1(hex(int(entry2.get()))[2:]) # 长度1B
print("设备地址", devide_add)
post_time = left_zero_hex1(hex(int(entry4.get()))[2:]) # 长度1B
print("上传间隔", post_time)
pressor_start_delay = left_zero_hex1(hex(int(entry5.get()))[2:]) # 长度1B
print("延时", pressor_start_delay)
Temp = int(entry7.get()) # 1B
Temp_2 = '{:06b}'.format(Temp) # 温度转二进制
Temp_2 = '0' + Temp_2 + '0' # 拼成8位二进制
Temp = left_zero_hex1(hex(int(Temp_2, 2))[2:]) # 16进制温度最终数据
print("温度", Temp)
Temp_pc = left_zero_hex1(hex(int(entry8.get()))[2:]) # 1B 温度控制偏差
print("偏差", Temp_pc)
frame = devide_code + devide_add + "00" + post_time + pressor_start_delay + "0000" + Temp + Temp_pc + "ffff" + "ffff" + "00"
frame = build_frame("1c", "05", frame) # 构造参数帧
print(frame)
d = bytes.fromhex(frame)
ser.write(d)
print("帧号:",frame_num)
frame_num = frame_num + 1 # 帧号自增1
sleep(0.1)
root1 = Toplevel(root)
root1.title('设置')
root1.geometry('400x400')
root1.maxsize(400, 400)
root1.minsize(400, 400)
'''设备编码'''
label1 = Label(root1,text="设备编码:", height=1)
label1.place(x=120, y=5)
entry1 = Entry(root1, width=20, textvariable='')
entry1.place(x=180, y=5)
'''设备地址'''
label2 = Label(root1,text="设备地址:", height=1)
label2.place(x=120, y=30)
entry2 = Entry(root1, width=20, textvariable='')
entry2.place(x=180, y=30)
'''注册帧'''
label3 = Label(root1,text="发送注册帧定时时间(s)", height=1)
label3.place(x=50, y=60)
entry3 = Entry(root1, width=20, textvariable='')
entry3.place(x=180, y=60)
'''采集时间'''
label4 = Label(root1,text="采集时间间隔(s)", height=1)
label4.place(x=85, y=90)
entry4 = Entry(root1, width=20, textvariable='')
entry4.place(x=180, y=90)
'''压缩机启动延时'''
label5 = Label(root1,text="压缩机启动延时(s)", height=1)
label5.place(x=75, y=120)
entry5 = Entry(root1, width=20, textvariable='')
entry5.place(x=180, y=120)
'''设定温度'''
label7 = Label(root1,text="设定温度(℃)", height=1)
label7.place(x=100, y=180)
entry7 = Entry(root1, width=20, textvariable='')
entry7.place(x=180, y=180)
'''温度控制偏差'''
label8 = Label(root1,text="温度控制偏差(℃)", height=1)
label8.place(x=80, y=210)
entry8 = Entry(root1, width=20, textvariable='')
entry8.place(x=180, y=210)
root1.Button1 = Button(root1, command=build_data_frame, height=3, width=10, bg="skyblue", text="发送")
root1.Button1.place(x=100, y=280)
root1.Button2 = Button(root1, command=root1.destroy, height=3, width=10, text="取消")
root1.Button2.place(x=200, y=280)
root1.mainloop()
def openBox(): #抽屉锁控
def build_lock_frame(): # 功能号03
global box,frame_num
# 将box转为16进制保存
frame_num_re() #帧号合法性校验
#print("xx",left_zero_hex2(hex(int(box, 2))[2:]))
frame = build_frame("0c", "03", left_zero_hex2(hex(int(box, 2))[2:]))
d = bytes.fromhex(frame)
ser.write(d)
frame_num = frame_num + 1 # 帧号自增1
sleep(0.1)
class GUI:
def __init__(self):
self.root2 = tk.Toplevel(root)
self.root2.title('抽屉开锁控制')
self.root2.geometry("300x100")
self.interface()
def interface(self):
""""界面编写位置"""
self.Button0 = tk.Button(self.root2, text="确定", command=self.event)
self.Button0.grid(row=4, column=3)
self.v1 = tk.IntVar()
self.Checkbutton01 = tk.Checkbutton(self.root2, text="1号柜", command=self.Check_box, variable=self.v1)
self.Checkbutton01.grid(row=0, column=1)
self.v2 = tk.IntVar()
self.Checkbutton02 = tk.Checkbutton(self.root2, text="2号柜", command=self.Check_box, variable=self.v2)
self.Checkbutton02.grid(row=0, column=2)
self.v3 = tk.IntVar()
self.Checkbutton03 = tk.Checkbutton(self.root2, text="3号柜", command=self.Check_box, variable=self.v3)
self.Checkbutton03.grid(row=0, column=3)
self.v4 = tk.IntVar()
self.Checkbutton04 = tk.Checkbutton(self.root2, text="4号柜", command=self.Check_box, variable=self.v4)
self.Checkbutton04.grid(row=0, column=4)
self.v5 = tk.IntVar()
self.Checkbutton05 = tk.Checkbutton(self.root2, text="5号柜", command=self.Check_box, variable=self.v5)
self.Checkbutton05.grid(row=0, column=5)
self.v6 = tk.IntVar()
self.Checkbutton06 = tk.Checkbutton(self.root2, text="6号柜", command=self.Check_box, variable=self.v6)
self.Checkbutton06.grid(row=1, column=1)
self.v7 = tk.IntVar()
self.Checkbutton07 = tk.Checkbutton(self.root2, text="7号柜", command=self.Check_box, variable=self.v7)
self.Checkbutton07.grid(row=1, column=2)
self.v8 = tk.IntVar()
self.Checkbutton08 = tk.Checkbutton(self.root2, text="8号柜", command=self.Check_box, variable=self.v8)
self.Checkbutton08.grid(row=1, column=3)
self.v9 = tk.IntVar()
self.Checkbutton09 = tk.Checkbutton(self.root2, text="9号柜", command=self.Check_box, variable=self.v9)
self.Checkbutton09.grid(row=1, column=4)
self.v10 = tk.IntVar()
self.Checkbutton10 = tk.Checkbutton(self.root2, text="10号柜", command=self.Check_box, variable=self.v10)
self.Checkbutton10.grid(row=1, column=5)
#self.w1 = tk.Text(self.root2, width=20, height=10)
#self.w1.grid(row=5, column=0)
def event(self): # 绑定到开锁帧生成发送事件
global box
'''按钮事件,获取复选框的状态,1表示勾选,0表示未勾选'''
box = str(box7) + str(box6) + str(box5) + str(box4) + str(box3) + str(box2) + str(box1) + str(
box0) + "000000" + str(box9) + str(box8) # 保存当前锁控16位2进制
#print(box) 检验box序列
build_lock_frame() # 构建开锁帧
def Check_box(self):
'''复选框事件'''
global box0, box1, box2, box3, box4, box5, box6, box7, box8, box9
if self.v1.get() == 1: box0 = 1
if self.v1.get() == 0: box0 = 0
if self.v2.get() == 1: box1 = 1
if self.v2.get() == 0: box1 = 0
if self.v3.get() == 1: box2 = 1
if self.v3.get() == 0: box2 = 0
if self.v4.get() == 1: box3 = 1
if self.v4.get() == 0: box3 = 0
if self.v5.get() == 1: box4 = 1
if self.v5.get() == 0: box4 = 0
if self.v6.get() == 1: box5 = 1
if self.v6.get() == 0: box5 = 0
if self.v7.get() == 1: box6 = 1
if self.v7.get() == 0: box6 = 0
if self.v8.get() == 1: box7 = 1
if self.v8.get() == 0: box7 = 0
if self.v9.get() == 1: box8 = 1
if self.v9.get() == 0: box8 = 0
if self.v10.get() == 1: box9 = 1
if self.v10.get() == 0: box9 = 0
a = GUI() # a 是 GUI 类 的 一个 实例化 对象
a.root2.mainloop()
def tempControl():
def build_temp_frame(): # 功能号04
global frame_num
Temp = int(entry4.get())
Temp_2 = '{:06b}'.format(Temp) # 温度转二进制
Temp_2 = '0' + Temp_2 + '0' # 拼成8位二进制
Temp = hex(int(Temp_2, 2))[2:]
frame = build_frame("0B", "04", Temp)
d = bytes.fromhex(frame)
ser.write(d)
print("帧号:", frame_num)
frame_num = frame_num + 1 # 帧号自增1
sleep(0.1)
root3 = tk.Toplevel(root)
root3.title('温度控制')
root3.geometry('350x300')
root3.maxsize(350, 300)
root3.minsize(350, 300)
'''设备号'''
label1 = Label(root3,text="设备号:", height=1)
label1.place(x=80, y=30)
combo = ttk.Combobox(root3,width=17, height=8, justify=CENTER)
combo['values'] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
combo.bind("<<ComboboxSelected>>", lambda event: 'combo_handler(var=varBitrate.get())')
combo.current(0)
combo.place(x=140, y=30)
'''设定温度'''
label4 = Label(root3,text="设定温度(℃):", height=1)
label4.place(x=60, y=90)
entry4 = Entry(root3, width=20, textvariable='')
entry4.place(x=140, y=90)
root3.Button1 = Button(root3, command=build_temp_frame, height=3, width=10, bg="skyblue", text="发送")
root3.Button1.place(x=100, y=150)
root3.Button2 = Button(root3, command=root3.destroy, height=3, width=10, text="取消")
root3.Button2.place(x=200, y=150)
root3.mainloop()
def pressorControl(): # 压缩机开启控制 温度偏差36-37 温度设定64-65 采集温度66-67
global draw, Temp_recv,Temp_pc_recv,press_button # 采集温度 设定温度 温度控制偏差 均为int 10进制 直接操作
press_button =1#压缩机总开关
#采集温度±控制偏差 与设定温度不符合则开启压缩机,否则不用开启压缩机
if ((press_button==1)and(draw > Temp_recv + Temp_pc_recv)): # 开启压缩机
frame_num_re() # 帧号合法性校验
frame = build_frame("0b", "02", "01") # 构建发送帧 01表示开启
d = bytes.fromhex(frame)
ser.write(d) # 发送帧
if ((press_button==1)and((draw >= Temp_recv - Temp_pc_recv and draw <= Temp_recv + Temp_pc_recv)or(draw < Temp_recv - Temp_pc_recv))): # 在温控范围内
frame_num_re() # 帧号合法性校验
frame = build_frame("0b", "02", "00") # 构建发送帧 00表示关闭
d = bytes.fromhex(frame)
ser.write(d) # 发送
def pressorControl2(): # 压缩机关闭控制
global press_button # 压缩机总开关
press_button = 0
frame = "FFFF0B7601020060C8FFF7"
d = bytes.fromhex(frame)
ser.write(d)
##################控制函数区###################
#接收表
columns = ('帧长', '帧号', '地址', '功能号', '数据', '校验码')
tv = ttk.Treeview(root, show='headings', columns=columns,height=50)
tv.column('帧长', width=80, anchor='center')
tv.column('帧号', width=80, anchor='center')
tv.column('地址', width=80, anchor='center')
tv.column('功能号', width=80, anchor='center')
tv.column('数据', width=200, anchor='center')
tv.column('校验码', width=80, anchor='center')
tv.heading('帧长', text='帧长')
tv.heading('帧号', text='帧号')
tv.heading('地址', text='地址')
tv.heading('功能号', text='功能号')
tv.heading('数据', text='数据')
tv.heading('校验码', text='校验码')
tv.place(x=2,y=5,height=250,width=650)
#1号抽屉
b1 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b1.place(x=20, y=320)
b11= tk.Label(root,text="抽屉1")
b11.place(x=30,y=295)
#2号抽屉
b2 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b2.place(x=110, y=320)
b22 =tk.Label(root,text="抽屉2")
b22.place(x=120,y=295)
#3号抽屉
b3 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b3.place(x=200, y=320)
b33 =tk.Label(root,text="抽屉3")
b33.place(x=210,y=295)
#4号抽屉
b4 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b4.place(x=290, y=320)
b44 =tk.Label(root,text="抽屉4")
b44.place(x=300,y=295)
#5号抽屉
b5 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b5.place(x=380, y=320)
b55 =tk.Label(root,text="抽屉5")
b55.place(x=390,y=295)
#6号抽屉
b6 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b6.place(x=20, y=450)
b66=tk.Label(root,text="抽屉6")
b66.place(x=30,y=425)
#7号抽屉
b7 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b7.place(x=110, y=450)
b77 =tk.Label(root,text="抽屉7")
b77.place(x=120,y=425)
#8号抽屉
b8 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b8.place(x=200, y=450)
b88 =tk.Label(root,text="抽屉8")
b88.place(x=210,y=425)
#9号抽屉
b9 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b9.place(x=290, y=450)
b99 =tk.Label(root,text="抽屉9")
b99.place(x=300,y=425)
#10号抽屉
b10 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
b10.place(x=380, y=450)
b1010 =tk.Label(root,text="抽屉10")
b1010.place(x=390,y=425)
#压缩机状态
b11 =tk.Label(root,height=9,width=7,bg="red",text="OFF")
b11.place(x=480,y=350)
b1111 =tk.Label(root,text="压缩机\n状态")
b1111.place(x=485,y=310)
#温度监控
a1=tk.Label(root,text="【设定温度】")
a1.place(x=560,y=360)
global Temp_recv,draw,Temp_pc_recv
a11=tk.Label(root,text=str(Temp_recv)+'℃')
a11.place(x=585,y=380)
a2=tk.Label(root,text="【实时温度】")
a2.place(x=560,y=410)
a22=tk.Label(root,text=str(draw)+'℃')
a22.place(x=585,y=430)
a3=tk.Label(root,text="【偏差控制】")
a3.place(x=560,y=460)
a33=tk.Label(root,text=str(Temp_pc_recv)+'℃')
a33.place(x=585,y=480)
#实时接收函数
def recv_data(): #实时接收数据并更新各项信息
global recv,draw,box_recv,press_recv,Temp_recv,draw,Temp_pc_recv,press_button,frame_num
def box_check(): #抽屉总体定时情况更新
state_10 = int(box_recv, 16)
data = '{:016b}'.format(state_10) # 补齐16位的进制转换
#print(data) #8+7+6+5+4+3+2+1+000000+10+9 #顺序
#更新主界面抽屉锁状态
if data[7] == '0': b1.config(bg="red",text="OFF")
if data[7] == '1': b1.config(bg="green",text="ON")
if data[6] == '0': b2.config(bg="red",text="OFF")
if data[6] == '1': b2.config(bg="green",text="ON")
if data[5] == '0': b3.config(bg="red",text="OFF")
if data[5] == '1': b3.config(bg="green",text="ON")
if data[4] == '0': b4.config(bg="red",text="OFF")
if data[4] == '1': b4.config(bg="green",text="ON")
if data[3] == '0': b5.config(bg="red",text="OFF")
if data[3] == '1': b5.config(bg="green",text="ON")
if data[2] == '0': b6.config(bg="red",text="OFF")
if data[2] == '1': b6.config(bg="green",text="ON")
if data[1] == '0': b7.config(bg="red",text="OFF")
if data[1] == '1': b7.config(bg="green",text="ON")
if data[0] == '0': b8.config(bg="red",text="OFF")
if data[0] == '1': b8.config(bg="green",text="ON")
if data[15] == '0': b9.config(bg="red",text="OFF")
if data[15] == '1': b9.config(bg="green",text="ON")
if data[14] == '0': b10.config(bg="red",text="OFF")
if data[14] == '1': b10.config(bg="green",text="ON")
if press_recv == '00':b11.config(bg="red",text="OFF")
if press_recv == "01":b11.config(bg="blue",text="预启动")
if press_recv == "02":b11.config(bg="green",text="ON")
if press_recv == "03":b11.config(bg="yellow",text="故障")
##################实时检测启停压缩机################
if ser.isOpen(): #持续监听函数
print("open")
while True: #循环接收数据包
n = ser.inWaiting()
if n:
sleep(0.1)
n = ser.inWaiting()
payload = ser.read(n)
data = str(binascii.b2a_hex(payload))[2:-1]
if(crc16_xmodem(data[4:-8])==data[-8:-4]): #接收的数据进行crc合法性校验
if (len(data) == 88): #上传状态帧
print("上传状态帧:", data)
#print("crc计算值",crc)
#print("crc切片值",data[-8:-4])
#######数据处理##########
box_recv=data[72:76] #用于更新开锁状态
press_recv=data[62:64] #用于更新压缩机状态
#print("press",press_recv)
box_check() #更新快递柜状态
data_num=data[6:8] #帧号16转10
data_num = str(int(data_num, 16))
data_add=data[8:10] #设备地址16转10
data_add = str(int(data_add,16))
data_function= "Status" #功能号16转10
data_crc=data[80:84] #crc校验码
data_payload=data[12:30]+"..." #只显示部分数据帧
Temp_pc_recv=int(data[36:38],16) #温度控制偏差返回值 16进制
#print("控制偏差",Temp_pc_recv)
#----------采集温度转换-------------
#print("采集温度",data[66:68])
drawx='{:08b}'.format(int(data[66:68],16))
if(drawx[0]=='0'): #符号为非负
draw=int(drawx[1:7],2) #可用于画图的采集温度
if(drawx[0]=='1'): #符号为负
draw = int(drawx[1:7], 2) # 可用于画图的采集温度
draw= draw -2*draw #负数取相反数
#print("采集温度",draw)
# ----------采集温度转换-------------
# ----------设定温度转换-------------
#print("设定温度",data[64:66])
Temp_recv='{:08b}'.format(int(data[64:66],16))
Temp_recv = int(Temp_recv[1:7],2) #可用于画图的采集温度
#print("设定温度",Temp_recv)
# ----------设定温度转换-------------
##########压缩机控制###########
if ((press_button==1)and(draw > Temp_recv + Temp_pc_recv)): # 开启压缩机
frame_num_re() # 帧号合法性校验
frame = build_frame("0b", "02", "01") # 构建发送帧 01表示开启
d = bytes.fromhex(frame)
ser.write(d) # 发送帧
if ((press_button==1)and((draw >= Temp_recv - Temp_pc_recv and draw <= Temp_recv + Temp_pc_recv) or (
draw < Temp_recv - Temp_pc_recv))): # 在温控范围内
frame_num_re() # 帧号合法性校验
frame = build_frame("0b", "02", "00") # 构建发送帧 00表示关闭
d = bytes.fromhex(frame)
ser.write(d) # 发送
##########压缩机控制###########
########温度面板更新###########
a11.config(text=str(Temp_recv)+'℃')
a22.config(text=str(draw)+'℃')
a33.config(text=str(Temp_pc_recv)+'℃')
########温度面板更新###########
#######数据处理##########
tv.insert("", 0, values=("44",data_num,data_add,data_function,data_payload,data_crc))
if (len(data) == 28): #显示ACK帧
print("Ack帧:", data)
#######数据处理##########
data_num=data[6:8] #帧号16转10
data_num = str(int(data_num, 16))
data_add=data[8:10] #设备地址16转10
data_add = str(int(data_add,16))
data_function= "Ack" #功能号16转10
data_payload=data[12:]+"..."
data_crc2 = data[20:24] # crc校验码
#######数据处理##########
tv.insert("", 0, values=("28",data_num,data_add,data_function,data_payload,data_crc2))
else:
print(" not open")
t2=threading.Thread(target=recv_data) #单开线程用于运行while true接收,防止主界面与接收冲突
t2.start()
tv.insert("", 0, values=("default", "default", "default", "default", "FFFF0B7601020160C8FFF7"))
tv.Button2 = Button(root, text="压缩机控制", command=pressorControl,height=2,width=13)
tv.Button2.place(x=670,y=5)
tv.Button8 = Button(root, text="压缩机关闭", command=pressorControl2,height=2,width=13)
tv.Button8.place(x=670,y=75)
tv.Button5 = Button(root, text="打开抽屉 ", command=openBox,height=2,width=13)
tv.Button5.place(x=670, y=145)
tv.Button6=Button(root,text="设置设备参数",command=paramControl,height=2,width=13)
tv.Button6.place(x=670, y=215)
tv.Button7=Button(root,text="绘制温度曲线",command=lambda:draw_temp(),height=2,width=13)
tv.Button7.place(x=670, y=285)
tv.Button4 = Button(root, text="设置温度 ", command=tempControl,height=2,width=13)
tv.Button4.place(x=670, y=355)
root.mainloop()
if __name__ == "__main__":
xitong()