目录

前言:

温箱介绍:

开始编码工作:

官方文档解读:

(1)发送的数据格式介绍:

(2)发送数据举例:

构建转换进制的类:

构建读取温箱温度的方法:

(1)按照文档规则拼接命令:

(2)将温箱返回的值ASCII值转10进制:

(3)读取当前温度:

写在最后:

总结:

源码:


前言:

由于最近进了新公司,一个台资企业,做通信的硬件产品。在硬件产品的测试过程中需要用到温箱,故而需要远程控制温箱,实现全自动化测试;所以这边文章主要介绍一下如何使用python来控制 TEMI 1000这个型号的温箱。以下内容适用于TEMI 1000, TEMI 1500型号的温箱。

温箱介绍:

该温箱是我司从昆山振弘检测设备有限公司采购的TEMI 1000,但是控制器用的是韩国的,关于控温箱这个项目,可参考的资料仅有一份官方的manual,里面包含一些命令格式啥。只要你有温箱,就应该可以向厂家索取该文档。

该温箱的通讯接口只有一个RS232接口,要想实现远程控制,需要有串口转USB的线;类似于下图

所以,想依靠python来与之进行通信,还是要通过python 的pyserial库,关于怎么安装pyserial,过于简单,这里不做赘述,可自行百度。

开始编码工作:

官方文档解读:

(1)发送的数据格式介绍:

其实,在编码工作开始之前,必须熟读官方文档,首先要知道发送数据的格式,规则;比如,在温箱文档中这样描述发送数据的格式:

要想给温箱发送数据,必须遵循此格式,其中①②④⑦⑧都是固定的值,不用更改,②的设备地址默认是01,所以我们要拼凑的数据只有③⑤⑥了,下面我来一步一步分析。

③代表通信命令的类型,根据文档,命令有以下表中几种类型:常用的也就是RSD,WSD两种

⑤代表你要发送的数据

⑥是一个SUM值,需要计算后填充至命令中,根据文档的意思是将①之后,也就是“STX”之后的所有内容转十六进制相加,相加后转ASCII码,取ASCII码的后两位就是我们所需要的SUM值

(2)发送数据举例:

由文档的示例可知,需拼接:STX + 设备地址(默认01)+命令(读用RSD,写用WSD)+ 逗号+寄存器个数+寄存器地址+计算后的SUM值+CR+LF。

拼接的结果用ASCII表示就是:

STX01RRD,02,0001,0002B2CRLF

转成十六进制表示就是:

02 30 31 52 53 44 2C 30 32 2C 30 30 30 32 42 32 0D 0A

上面的ASCII值中B2就是通过从STX后的值到B2前面的值转16进制相加,再转ASII取后两位的结果得出B2,这里的算法可参考文章末尾源码中的def _calculate_sum 函数。

到此,关于发送的数据格式和规则介绍完了,接收的数据也是差不多的原理。接下来开始上代码。

构建转换进制的类:

官方文档熟读后得知,与该温箱通信需要进行几种进制的转换,所以先封装转换方法为Converter类,具体见注释:

class Converter(object):  @staticmethoddef to_ascii(h):  # 转换str为ascii值list_s = []for i in range(0, len(h), 2):list_s.append(chr(int(h[i:i + 2], 16)))return ''.join(list_s)@staticmethoddef to_hex(s):  # 转换str为十六进制值list_h = []for c in s:list_h.append(str(hex(ord(c))[2:]))return ''.join(list_h)@staticmethoddef convert_hex_to_dec(hexTemp):  # 转换十六进制为10进制的温度值temp = int(hexTemp, 16)return -(temp & 0x8000) | (temp & 0x7fff)@staticmethoddef convert_dec_to_hex(decTem):  # 转10进制为16进制if decTem >= 0:return hex(decTem)[2:].upper().zfill(4)else:binStr = bin(decTem & 0xFFFF)[2:]hexStr = hex(int(binStr, 2))[2:].upper()return hexStr

构建读取温箱温度的方法:

(1)按照文档规则拼接命令:

 def _splicing_command(self, register, registerCount=1, cmdType=CommandType.RSD, data=None):register = Utils.to_enum(register, ModbusRegister).valueregisterCount = int(registerCount)cmdType = Utils.to_enum(cmdType, CommandType).namesumStr = self._calculate_sum(cmdType, registerCount, register, data)if register >= 1000:register = str(register)else:register = str(register).zfill(4)# convert to hexheader = '02'address = Converter.to_hex('01')cmdType = Converter.to_hex(cmdType)registerCount = Converter.to_hex('01')register = Converter.to_hex(register)comma = Converter.to_hex(',').upper()sumStr = Converter.to_hex(sumStr)CR = '0D'LF = '0A'if data is not None:data = Converter.to_hex(data)command = ''.join([header, address, cmdType, comma, registerCount, comma, register, comma, data, sumStr, CR, LF])else:command = ''.join([header, address, cmdType, comma, registerCount, comma, register, sumStr, CR, LF])return command

(2)将温箱返回的值ASCII值转10进制:

 def _calculate_temp(self, response):resp = str(response).split(',')hexTemp = resp[-1][:4]temp = Converter.convert_hex_to_dec(hexTemp) / 100return round(temp, 2)

(3)读取当前温度:

def get_current_temperature(self, register=ModbusRegister.TEMP_NPV):command = self._splicing_command(register)self._ser.open_port_connection(self._ser.get_usb_to_serial_port(), baudrate=9600)self._ser.write_data('{:s}'.format(command))self._common.pause(1, reason='Wait TEMI1000 response')resp = self._ser.read_data()self._ser.close_port_connection()self._check_response(resp)temp = self._calculate_temp(resp)return temp

写在最后:

总结:

由此可见,关于温箱的控制,难点在于了解数据通信的规则,以及各种进制的转换,后期我打算用tkinter模块写一个GUI程序,将温箱的控制集成进去,感兴趣的伙伴可以持续关注哈!

对了,附一下项目整体的源码吧,源码中没有中文注释,就这几句也是现加的,因为,好的代码不需要注释别人也能看懂!感兴趣的可私聊!该博客会持续更新

源码:

# 以下是导入一些现有的模块,枚举类,以及util类,等等
import timefrom ..enums.tempbox import CommandType
from ..enums.tempbox import RespStatusCode
from ..enums.tempbox import ModbusRegister
from ..enums.tempbox import RunningStatus
from .serialext import _SerialKeywords
from ._context import _Context
from .common import _CommonKeywords
from .seleniumext import _SeleniumExtKeywordsfrom robot.api import logger
from robot.utils import secs_to_timestr
from robot.utils import timestr_to_secsfrom ..utils import Utils# 以下是封装的进制转换的类class Converter(object):@staticmethoddef to_ascii(h):list_s = []for i in range(0, len(h), 2):list_s.append(chr(int(h[i:i + 2], 16)))return ''.join(list_s)@staticmethoddef to_hex(s):list_h = []for c in s:list_h.append(str(hex(ord(c))[2:]))return ''.join(list_h)@staticmethoddef convert_hex_to_dec(hexTemp):temp = int(hexTemp, 16)return -(temp & 0x8000) | (temp & 0x7fff)@staticmethoddef convert_dec_to_hex(decTem):if decTem >= 0:return hex(decTem)[2:].upper().zfill(4)else:binStr = bin(decTem & 0xFFFF)[2:]hexStr = hex(int(binStr, 2))[2:].upper()return hexStrclass _TempKeywords(_Context):  # 这里是所有温箱实现的功能def __init__(self, *args):super().__init__(*args)self._common = _CommonKeywords(*args)self._ser = _SerialKeywords(*args)self._sel = _SeleniumExtKeywords(*args)# region Data Acquisitiondef get_current_temperature(self, register=ModbusRegister.TEMP_NPV):  # 读取温箱现在的温度,并打印出来resp = self._read_content(register)temp = self._calculate_temp(resp)logger.info('The temperature of the TEMI1000 is {:.2f} C now'.format(temp))return tempdef get_the_configured_temperature(self, register=ModbusRegister.TEMP_NSV):# 获取温箱当前设定的定值是多少resp = self._read_content(register)temp = self._calculate_temp(resp)logger.info('The TEMI1000 configured temperature is {:.2f} C'.format(temp))return tempdef get_running_state(self, register=ModbusRegister.NOWSTS):  # 获取温箱现在的运行状态,是正在升降温还是在待机resp = self._read_content(register)code = str(resp).split(',')[-1].strip()[:-2]state = Utils.to_enum(int(code), RunningStatus)logger.info('The TEMI1000 running status is: {:s}'.format(state))return state# endregion# region Functiondef set_temperature_output(self, temp, register=ModbusRegister.FIX_TEMP_TSP):  # 设置温度输出,并激活输出temp = int(temp) * 100targetTemp = Converter.convert_dec_to_hex(temp)state = self.get_running_state()if state is RunningStatus.ON:self.start_stop_trigger()self._write_content(data=targetTemp, register=register)self.activate_output()def start_stop_trigger(self, data='0001', register=ModbusRegister.COM_OPMODE):  # 开始激活输出的底层函数self._write_content(data=data, register=register)def deactivate_temperature_output(self): # 停止温度输出,回到待机状态state = self.get_running_state()if state is RunningStatus.ON:self.start_stop_trigger()else:logger.info('Running status is already {}'.format(RunningStatus.ON.name))def activate_temperature_output(self): # 激活温度输出的函数state = self.get_running_state()if state is RunningStatus.ON:logger.info('Running status is already {}'.format(RunningStatus.ON.name))else:self.start_stop_trigger()def wait_until_temperature_is(self, temp, timeout=3600, interval=5): # 等待,等待温箱升降温到设定的值temp = int(temp)timeout = timestr_to_secs(timeout)interval = timestr_to_secs(interval)timeoutAbs = time.time() + timeoutwhile time.time() < timeoutAbs:self._sel.fragment_sleep(interval)currentTemp = self.get_current_temperature()if Utils.floatEquals(currentTemp, temp, uncertainty=0.2):logger.info('The current temperature reaches the target temperature: {}'.format(temp))return currentTempraise AssertionError('The temperature not reaches the target temperature after {:s}'.format(secs_to_timestr(timeout)))# endregiondef _check_response(self, response): # 对温箱的返回内容进行检查,异常的话则报错response = str(response)if 'OK' in response:logger.info('Returned response is OK')elif 'NG' in response:errCode = int(response.split('NG')[-1][:2])errInfo = Utils.to_enum(errCode, RespStatusCode)raise AssertionError('Response failed :{:s}'.format(errInfo.name))else:raise AssertionError('Command send failed, response :{:s}'.format(response))def _splicing_command(self, register, registerCount=1, cmdType=CommandType.RSD, data=None): # 拼接读写数据的命令register = Utils.to_enum(register, ModbusRegister).valueregisterCount = int(registerCount)cmdType = Utils.to_enum(cmdType, CommandType).namesumStr = self._calculate_sum(cmdType, registerCount, register, data)if register >= 1000:register = str(register)else:register = str(register).zfill(4)# convert to hexheader = '02'address = Converter.to_hex('01')cmdType = Converter.to_hex(cmdType)registerCount = Converter.to_hex('01')register = Converter.to_hex(register)comma = Converter.to_hex(',').upper()sumStr = Converter.to_hex(sumStr)CR = '0D'LF = '0A'if data is not None:data = Converter.to_hex(data)command = ''.join([header, address, cmdType, comma, registerCount, comma, register, comma, data, sumStr, CR, LF])else:command = ''.join([header, address, cmdType, comma, registerCount, comma, register, sumStr, CR, LF])return commanddef _calculate_sum(self, cmdType, registerCount, register, data=None):  # 温箱发送的数据中需要对部分数据先处理,计算出一个SUM值,然后将要发送的数据拼接上SUM值一起发送给温箱,温箱才能正确相应,这部分也是一个难点"""1) Add the ASCII code of characters from the character next to STX one by one up to the character prior to SUM2) Represent the lowest one byte of the sum as a hexadecimal notation (2 characters)"""registerCount = int(registerCount)cmdType = Utils.to_enum(cmdType, CommandType).nameregister = Utils.to_enum(register, ModbusRegister).valueif register >= 1000:register = str(register)else:register = str(register).zfill(4)comma = ['2C']address = [Converter.to_hex(i) for i in '01']registerCount = [Converter.to_hex(i) for i in str(registerCount).zfill(2)]register = [Converter.to_hex(i) for i in register]cmdType = [Converter.to_hex(i) for i in str(cmdType)]if data is not None:data = [Converter.to_hex(i) for i in data]if data is not None:cmdStr = address + cmdType + comma + registerCount + comma + register + comma + dataelse:cmdStr = address + cmdType + comma + registerCount + comma + registersumStr = ','.join(cmdStr)SUM = hex(sum([int(v, 16) for v in sumStr.split(',')]))[-2:]return SUM.upper()def _calculate_temp(self, response): # 将温箱返回的内容转换成十进制温度resp = str(response).split(',')hexTemp = resp[-1][:4]temp = Converter.convert_hex_to_dec(hexTemp) / 100return round(temp, 2)def _write_content(self, data, register):  # 封装写数据的通用方法command = self._splicing_command(register, cmdType=CommandType.WSD, data=data)port = self._ser.get_usb_to_serial_port()self._ser.open_port_connection(port, baudrate=9600)self._ser.write_data('{:s}'.format(command))self._common.pause(1, reason='Wait TEMI1000 response')resp = self._ser.read_data()self._ser.close_port_connection()self._check_response(resp)return respdef _read_content(self, register): # 封装读取数据的通用方法command = self._splicing_command(register)self._ser.open_port_connection(self._ser.get_usb_to_serial_port(), baudrate=9600)self._ser.write_data('{:s}'.format(command))self._common.pause(1, reason='Wait TEMI1000 response')resp = self._ser.read_data()self._ser.close_port_connection()self._check_response(resp)return resp

python 使用pyserial控制温箱(附源码)相关推荐

  1. 疯狂python讲义视频 百度云-疯狂Python讲义 PDF高清版附源码

    内容简介 本书全面,深入地介绍了Python编程的相关内容,大致可分为四个部分.*系统部分介绍了Python的基本语法结构,函数编程,类和对象,模块和包,异常处理等: 第二部分主要介绍Python常用 ...

  2. Python实现飞机大战-第二部分(附源码、素材、超详细教程)

    飞机大战第二部分 1.前言 2.飞机射击 2.1.添加子弹的相关设置 2.2.创建文件bullet.py 2.3.修改game_functions.py 2.4.修改mian.py 2.5.射击效果 ...

  3. 熬夜整理出了70个清华大佬都在用的Python经典练手项目【附源码】

    我们都知道,不管学习那门语言最终都要做出实际的东西来,而对于编程而言,这个实际的东西当然就是项目啦,不用我多说大家都知道学编程语言做项目的重要性. 于是,小编熬了几个通宵,终于整理出了70个清华大佬都 ...

  4. Python实战例子(32个附源码)

    Python是一种高级编程语言,具有简洁.清晰的语法,易于理解和使用,因此受到广泛的欢迎.尤其在数据科学.人工智能.机器学习.自然语言处理等领域,Python已成为最受欢迎的编程语言之一.Python ...

  5. Python人物头像动漫化[附源码]!!

    前文 哈哈哈乍一眼看到的第一眼的这张小姐姐图是不是很好看,缩小的是图片的原图,放大的是漫画的图片哦!!当你正在为你换什么头像烦恼时,看看这篇文章叭! 当你有对象的时候还可以用你的女朋友的动漫化的卡通形 ...

  6. 100个Python实战练手项目(附源码+素材),学习必备

    前言: 不管学习哪门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦,不用多说大家都知道学编程语言一定要做项目才行. 这里整理了最新32个Python实战项目列表,都有完整且详细的视频教程和 ...

  7. 70个Python实用练手项目(附源码)

    不管学习哪门语言都要做出实际的东西来,这个实际的东西就是项目. 恶霸整理了 70 个 Python 实战项目,都有完整且详细的教程,你可以从中选择自己想做的项目进行参考学习练手,你也可以从中寻找灵感去 ...

  8. 【中秋系列】卧槽,厉害了,用Python竟能做巧克力月饼。[附源码]

    导语 掐指一算 ​ 2021中秋节只有14天了! 家家户户中秋都会赏月吃月饼滴 相聚不容易,重在一个"团圆美满" BUT  小编从小到大都不喜欢吃月饼撒,在家的画,家里都会买,每年 ...

  9. 用Python写了个绝地求生(附源码)

    作者:LookOutTheBush https://juejin.cn/post/7232253274056785957 仅做学习交流,非盈利 一.概述 1.1 效果 总的来说,这种方式是通过图像识别 ...

最新文章

  1. 激励和设定可实现的目标是任何学习成功的关键。
  2. nginx alias php 404,Nginx配置静态服务器以及404问题解决,root和alias的区别
  3. java 可能尚未初始化变量,java - 局部变量“变量”可能尚未初始化-Java - 堆栈内存溢出...
  4. 第三季度营收不及预期 拼多多收盘跌超15%
  5. 基于特定实体的文本情感分类总结(PART II)
  6. Spark集群无法停止的原因分析和解决
  7. Java 正则表达式的用法与实例
  8. 手把手教学华为手机利用ABD工具卸载/禁用(隐藏)/恢复系统自带软件!
  9. java 模板组件_2-1 模块组件模板目录层级及简单定义
  10. 克隆硬盘后进不去系统_硬盘GHOST克隆后,为什么新的硬盘系统启动不了?
  11. [随心译]2017.8.5-你家毛茸茸的宠物的荤粮正在加速气候变化
  12. 编程猫python教学_有老师使用过编程猫进行教学吗?
  13. libdbus 实例以及使用d-feet查看接口方法
  14. nested exception is com.google.gson.JsonSyntaxException: com.google.gson.stream-异常解决!!!
  15. 深度学习AI美颜系列----人像静态/动态贴纸特效算法实现
  16. java异常处借接错书_Java入门第三季-异常-图书馆借书系统
  17. 达摩院技术创新全景|懂你的语音AI
  18. 像容忍自己一样容忍他人
  19. VUE仿猫眼电影总结(一)
  20. 算法图解[Aditya Bhargava]--读书笔记

热门文章

  1. 沉病孩子留遗嘱 父疏申请接济劫持红十字员农
  2. python表情包多样化图形化聊天室_Python还有这功能:自动生成表情包,从此斗图无敌手!...
  3. Abp Core 添加短信验证码登录(动态密码登录)
  4. (病毒安全)任何杀毒软件都不能用了
  5. 项目管理sod_Microsoft Visual SourceSafe(项目文件管理) V6.0 最新中文版(图文)
  6. Vue-router的使用
  7. 点击识别元素位置、生成显示二维码、下载二维码
  8. Spring学习笔记之MyBatis
  9. 视频剪辑-OpenShot
  10. 封号、扣钱:谁来保证专车司机的利益?