python实现串口通信的示例代码


Posted in Python onFebruary 10, 2020

1 硬件设备

  • TTL串口摄像头(VC0706)
  • USB转TTL烧录器

2 serial安装

第一次安装的是serial的包导包的时候发现下载错了,正确应该是pyserial。安装后直接import就可以了。

3 实现串口通信

3.1 发现端口

Windows下为COM(N, N=1、2...), Ubuntu下为‘/dev/ttyS0'Windows初学者,可以给您一下两种方式确定端口号。

方法一:输入在终端(cmd)中输入

python -m serial.tools.list_ports

输出结果:

COM5
1 ports found

方法二:搜索电脑上的设备管理器,打开以后然后插入烧录器,自动就会弹出。如果没有弹出就可能是驱动没有安装,安装好以后不好使,重启一下电脑,到了工作的时候大家都知道程序员会跟你说,你重启一下,清一下缓存,这两句话。也有可能是驱动安装的不对。

python实现串口通信的示例代码

方法三:直接找一个有端口扫描的上位机,点击扫描就可以了。大部分上位机都是你一插进去就会检测到你的端口。

python实现串口通信的示例代码----->python实现串口通信的示例代码

注意:当串口被占用的时候也有可能导致失败,例如你在编译器有两个进程运行下面的测试代码,第二个进程就会因为端口占用而失效。也有的上位机是因为同时打开了两个上位机的缘故(实验课的时候同学遇到过情况),可以用任务管理器kill掉。

测试:

import serial
 
 
#Windows
ser = serial.Serial(port='COM5', baudrate=115200, timeout=0.5)
print(ser.name)

控制台打印结果: 

COM5
Process finished with exit code 0

建立ser对象的代码:

class PicSerial:
  __ser = None # ser的单例
  __isinit = False
 
  @staticmethod
  def get_available_port():
    """
    检测可以使用的端口号
    :return->str: 端口号的名称
    """
    port = list(list_ports.comports())
    if len(port) > 0:
      port_name = port[0].device
      print(port_name)
      return port_name
      # logging.info("Available port:", ports)
    else:
      print("There is no available port.")
      # logging.error("There is no available port.")
 
  def __new__(cls, *args, **kwargs):
    if PicSerial.__ser is None:
      cls.__ser = object.__new__(cls)
    return cls.__ser
 
  def __init__(self):
    if not PicSerial.__isinit:
      self.sername = self.get_available_port()
      self.ser = serial.Serial(port=self.sername, baudrate=BAUDRATE)
      PicSerial.__isinit = False
      print("PicSerial init.")

3.2 发送命令

3.2.1 协议格式

python实现串口通信的示例代码

3.2.2 serial传送的方式

serial传送的方式有:

串行端口对象。只传单个字节。字符串。字节数组+字节数组长度。

所以直接选用数组传数据,这里会遇到一个问题就是python的list会自动把十六进制数转换为整形

python实现串口通信的示例代码

所以要进行转换你可以直接写成b“/x56/x00/x17/x00”。假如你不需要传十进制也可以转成list,直接map(chr,x)或map(ord,x)也是可以的。读的时候也要注意只要你放进list里面就会自动转成整形。

【我觉得这样写很降智,但是又不得不这样写】

#在PicSerial中
  def isreply(self, cmd: bytes, option: str) -> bool:
    """
    检测是否有回复
    :return:回复的内容
    :param cmd:
    :param option:
    :return: True则有回复
    """
    if isinstance(cmd, bytes) and isinstance(option, str) and len(cmd) > 0 and len(option) > 0:
      self.ser.write(cmd)
      reply = self.ser.read(4)
      reply = list(map(chr, list(reply)))
      print("49h,The function'{}' is running. reply:{}".format(sys._getframe().f_code.co_name, reply))
      if len(reply) >= 4 and reply[0] == 'v' and reply[1] == SERIAL_NUM and reply[2] == option and reply[3] == STATUS:
        return True
    return False

测试:

#在test文件中
class TestSerial(unittest.TestCase):
  def test_isreply(self):
    self.assertTrue(ser.isreply(GET_VERSION_CMD, VERSION))
    self.assertFalse(ser.isreply('\x56\x00\x11\x00', VERSION))
    self.assertFalse(ser.isreply(GET_VERSION_CMD, b'\x11'))
    self.assertFalse(ser.isreply(123456, b'\x11'))
    self.assertFalse(ser.isreply('', VERSION))
    self.assertFalse(ser.isreply(b'', VERSION))
    self.assertFalse(ser.isreply(GET_VERSION_CMD, ''))
    self.assertFalse(ser.isreply(GET_VERSION_CMD, None))
    self.assertFalse(ser.isreply(b'', ''))
    self.assertFalse(ser.isreply(b'\x56\x00\xAA\x00', VERSION))
    self.assertFalse(ser.isreply(GET_VERSION_CMD, '\xAA'))
 
 
#之后就省略不写了
if __name__ == '__main__':
  unittest.main()

结果:

python实现串口通信的示例代码

3.3 获取版本号(hello world)

按照协议一步一步操作

主 机 发:56 00 11 00 摄像头回:76 00 11 00 0B 56 43 30 37 30 33 20 31 2E 30 30 (VC0703 1.00)

#在PicSerial中
  def getversion(self) -> str:
    """
    获取版本号
    :return:
    """
    cmd = GET_VERSION_CMD
    option = VERSION
    if self.isreply(cmd, option):
      left = self.ser.readall()
      print("75h,The function'{}' has responded.left{}".format(sys._getframe().f_code.co_name, left))
      return self.ser.read(12).decode()[1:]

测试:

#在test文件中
  def test_getversion(self):
    self.assertEqual(ser.getversion(), 'VC0703 1.00')

结果:通过测试

3.4 复位

主 机 发: 56 00 26 00

摄像头回: 76 00 26 00 00

#在PicSerial中
  def reset(self):
    """
    复位
    :return:
    """
    cmd = REST_CMD
    option = RESET
    if self.isreply(cmd, option):
      if self.ser.read(1) == b'':
        left = self.ser.readall()
        print("75h,The function'{}' has responded.left{}".format(sys._getframe().f_code.co_name, left))
        return True
    return False

*测试和运行结果不一样。

python实现串口通信的示例代码

python实现串口通信的示例代码

花了一点时间找到原因了,单元测我都是点击前面绿色的小箭头,以为只是运行当前的测试函数的内容,但是我发现它把其他的函数都运行了。所以要把之前的测试函数注释掉得到的结果就一样了。

python实现串口通信的示例代码

测试通过。

3.5 照相

  • 停止当前帧刷新
  • 获娶图片长度
  • 获取图片
  • 恢复帧更新

3.5.1 停止当前帧刷新

这一步执行一次就够了。因为读命令的时候会出现麻烦。但是这一步是有意义的,就是当你发现图片很大,的时候正常大小就两个byte可以表示完了(排除你的图片面积十分大或十分清晰),又或者是突然读空了。假如数值非常的大,可以使用该函数,再不行就要选择复位。

def stoprefresh(self):
    """
    停止刷新当前帧
    :return:
    """
    cmd = STOP_REFRESH_CMD
    option = TAKE_PHOTO
    self.ser.write(cmd)
    if self.isreply(cmd, option) and self.ser.read(1) == b"\x00":
      left = self.ser.readall()
      print("87h,The function'{}' has responded.left{}".format(sys._getframe().f_code.co_name, left))
      return True
    return False

通过测试

def test_stoprefresh(self):
    self.assertTrue(ser.stoprefresh())

3.5.2 获娶图片长度

def getlength_bytes(self) -> bytes:
    """
    获取图片的长度
    :return:
    """
    cmd = GET_LENGTH_CMD
    option_pic = '4'
    self.ser.write(cmd)
    if self.isreply(cmd, option_pic):
      if self.ser.read(1) == b'\x04':
        res = self.ser.read(4)
        left = self.ser.readall()
        print("103h,The function'{}' has responded.left{}".format(sys._getframe().f_code.co_name, left))
        return res
    return b'\x00\x00\x00\x00'

测试通过

def test_getlength(self):
    self.assertEqual(ser.getlength(), b'\x00\x00\x12\x34')

3.5.3 恢复帧更新

def recover_refresh(self):
    """
    恢复帧刷新
    :return:
    """
    cmd = RECOVER_REFRESH_CMD
    option = TAKE_PHOTO
    self.ser.write(cmd)
    if self.isreply(cmd, option):
      # 读出剩余的字节
      left = self.ser.readall()
      print("142h,The function'{}' has responded.left{}".format(sys._getframe().f_code.co_name, left))
      return True
    return False

测试并通过:

def test_recover_refresh(self):
    self.assertTrue(ser.recover_refresh())

3.5.4 拍照

在这里卡了很长时间,不知道为什么长度是不确定的,每一次读的长度都没读完,看代码。

下面代码只是演示

#下面代码只是演示不在最终版本中 
  def savephoto(self, cmd, option, len):
    """
    保存图片
    :param cmd:
    :param option:
    :param len: 照片的长度
    :return:
    """
    with open('write_pic/serialpic/photo.jpg', 'wb') as f:
      if self.isreply(cmd, option):
        print(self.ser.read(1))
      countofread_complete_byte = 0 # 用于计算当前已经写入的长度
      while countofread_complete_byte != len + 10:
        # read()是有上限的,不可以把全部都读取
        lines = self.ser.read(len + 10 - countofread_complete_byte)
        countofread_complete_byte += lines.__len__()
        f.write(lines)
        print("142h,countofread_complete_byte:", countofread_complete_byte, "lines", lines.__len__())
      left = self.ser.readall()
      print("146h,少读内容:", left, "共", left.__len__(), "个字节")
    res = self.ser.readall()
    print(res)

现象:

现象是运行一直不停都是手动stop console,或者没有stop console会一直打印lines为空,就此可以猜测read()不是阻塞的。是图片字节总数不断增多。每次遍历完后满足self.ser.read(len + 10 - countofread_complete_byte)后再readall()还是有剩余的内容。

python实现串口通信的示例代码

python实现串口通信的示例代码

我发现此时readall一共读出了4049个字节,图片数据4030个字节+首尾两部分共10个字节,那多出来的9个字节是什么火眼金睛的Unyielding ● L发现了正确的开始位置为上图红色方块处,碰巧多出来的是九个字节,所以多出来的就不是这一张图片的内容,所以可以猜想程序没有停止的原因是上一次图片还没读完我就手动停止,所以留下了数据,上一次没有读完的内容,这一次读到了。

字节串和字符串都可以切片。直接切出来保存。

def getphoto(self):
    """
    拍照并且保存图片
    :return:
    """
    # self.reset()
    # 1、停止帧刷新
    # self.stoprefresh()
 
    # 获取图片长度
    # 返回字节长度用于整合命令,表示图片的总字节数
    length = self.getlength_bytes()
    # 返回整形,表示图片的总字节数
    len = self.bytesToInt(length)
    print("158h,len:", len)
 
    # 拍照
    cmd = GET_PHOTO_START_CMD + length + GET_PHOTO_END_CMD
    print("159hcmd", cmd)
    self.ser.write(cmd)
 
    readall = self.ser.readall()
    readall_len = readall.__len__()
    differ = readall_len - len - 10
    if differ != 0:
      res = readall[differ + 5:-5]
      print("172h:", res)
      self.savephoto(res)
    else:
      res = readall[5:-5]
      print("175h:", res)
      self.savephoto(res)
 
    # 关闭串口
    self.ser.close()
 
    # 恢复刷新
    # self.recover_refresh()

成功输出结果:

python实现串口通信的示例代码

为了方便debug,停帧回复帧都是手动发送的,剩下的问题就是把注释打开试一试能不能成功组合成一个函数,发现有的命令会读空,所以可以推断:一定又是前一个命令里面又留下来什么还没有被读取的字节造成读到的内容篡位了。

python实现串口通信的示例代码

每一次执行完命令后看一看还有没有遗留字节,把剩余字节都取出来,然后differ的判断都不需要了。【读者看到的代码都是最新版本的,此处我添加了left和print到对应函数中】,处理结果打印到控制台:

python实现串口通信的示例代码

def getphoto(self):
    """
    拍照并且保存图片
    :return:
    """
    # 1、停止帧刷新
    self.stoprefresh()
 
    # 获取图片长度
    # 返回字节长度用于整合命令,表示图片的总字节数
    length = self.getlength_bytes()
    # 返回整形,表示图片的总字节数
    len = self.bytesToInt(length)
    print("161h,The function'{}' is running. len:{}".format(sys._getframe().f_code.co_name, len))
 
    # 拍照
    cmd = GET_PHOTO_START_CMD + length + GET_PHOTO_END_CMD
    print("165h,The function'{}' is running. cmd:{}".format(sys._getframe().f_code.co_name, cmd))
    self.ser.write(cmd)
 
    readall = self.ser.readall()
    readall_len = readall.__len__()
    differ = readall_len - len - 10
    if differ != 0:
      res = readall[differ + 5:-5]
      print("161h,The function'{}' is running. res:{}".format(sys._getframe().f_code.co_name, res))
      self.savephoto(res)
    else:
      res = readall[5:-5]
      print("161h,The function'{}' is running. res:{}".format(sys._getframe().f_code.co_name, res))
      self.savephoto(res)
 
    # 恢复刷新
    self.recover_refresh()

输出图片结果(拍的是导线没有聚焦)

python实现串口通信的示例代码

python实现串口通信的示例代码

# 这个测试应该怎么写? 有图片就输出并且可以打开就可以了惹?有人能教教我?
  def test_getphoto(self):
    pass

python实现串口通信的示例代码

 4 反思犯了很多

‘我觉得'的错误,我觉得这个值是什么,多打断点看清楚,那一段演示代码里面因为协议写了是五个字节,isreply我已经读了4个字节再读一个一定是0x00,后来打印那一行返回的值是0x04这才为猜想读到上一张图作下铺垫。

当你写的很复杂超过20行的逻辑代码就知道一定是错了。--UnyieldingL

编码方面的内容耗费了很长时间,就在反思的时候发现了decode("hex")。惹?

list='aabbccddee' 
hexer=list.decode("hex") 
print hexer

打印日志要详细。 每一个变量涉及的变量长度函数名,此时哪个函数运行。

还有一个问题,pycharm还是没有自己停止,打印某个线程的堆栈。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python线程中对join方法的运用的教程
Apr 09 Python
Python输出PowerPoint(ppt)文件中全部文字信息的方法
Apr 28 Python
Python导入模块时遇到的错误分析
Aug 30 Python
小白如何入门Python? 制作一个网站为例
Mar 06 Python
pthon贪吃蛇游戏详细代码
Jan 27 Python
python二进制文件的转译详解
Jul 03 Python
Python中面向对象你应该知道的一下知识
Jul 10 Python
python pip安装包出现:Failed building wheel for xxx错误的解决
Dec 25 Python
如何使用PyCharm将代码上传到GitHub上(图文详解)
Apr 27 Python
python实现简单的井字棋游戏(gui界面)
Jan 22 Python
python tkinter模块的简单使用
Apr 07 Python
聊聊Python中关于a=[[]]*3的反思
Jun 02 Python
基于Tensorflow高阶读写教程
Feb 10 #Python
python集合删除多种方法详解
Feb 10 #Python
pandas中的数据去重处理的实现方法
Feb 10 #Python
对tensorflow中cifar-10文档的Read操作详解
Feb 10 #Python
基于Tensorflow:CPU性能分析
Feb 10 #Python
python sorted函数原理解析及练习
Feb 10 #Python
python pprint模块中print()和pprint()两者的区别
Feb 10 #Python
You might like
2020年4月新番动漫目录 官方宣布4月播出的作品一览
2020/03/08 日漫
php unset全局变量运用问题的深入解析
2013/06/17 PHP
PHP修改session_id示例代码
2014/01/08 PHP
Zend Framework数据库操作技巧总结
2017/02/18 PHP
javascript实现Table排序的方法
2015/05/15 Javascript
Bootstrap中定制LESS-颜色及导航条(推荐)
2016/11/21 Javascript
js仿百度音乐全选操作
2017/01/13 Javascript
JS三目运算(三元运算)方法详解
2017/03/01 Javascript
BootStrap表单控件之文本域textarea
2017/05/23 Javascript
关于javascript获取内联样式与嵌入式样式的实例
2017/06/01 Javascript
Angularjs 事件指令详细整理
2017/07/27 Javascript
vue.js实例对象+组件树的详细介绍
2017/10/20 Javascript
AngularJS中scope的绑定策略实例分析
2017/10/30 Javascript
require.js 加载过程与使用方法介绍
2018/10/30 Javascript
微信小程序实现跑马灯效果
2020/10/21 Javascript
详解关于element级联选择器数据回显问题
2019/02/20 Javascript
vue插件mescroll.js实现移动端上拉加载和下拉刷新
2019/03/07 Javascript
使用VueCli3+TypeScript+Vuex一步步构建todoList的方法
2019/07/25 Javascript
layui+jquery支持IE8的表格分页方法
2019/09/28 jQuery
vue使用nprogress实现进度条
2019/12/09 Javascript
javascript设计模式 ? 装饰模式原理与应用实例分析
2020/04/14 Javascript
解决vue打包 npm run build-test突然不动了的问题
2020/11/13 Javascript
[01:10:24]DOTA2-DPC中国联赛 正赛 VG vs Aster BO3 第一场 2月28日
2021/03/11 DOTA
深入理解Python3 内置函数大全
2017/11/23 Python
Python操作mongodb的9个步骤
2018/06/04 Python
python-pyinstaller、打包后获取路径的实例
2019/06/10 Python
python paramiko远程服务器终端操作过程解析
2019/12/14 Python
Qoo10马来西亚:全球时尚和引领潮流的购物市场
2016/08/25 全球购物
英国卫浴商店:Ergonomic Design
2019/09/22 全球购物
中专生自我鉴定书范文
2013/12/28 职场文书
赡养老人协议书
2014/04/21 职场文书
2015年学校减负工作总结
2015/05/19 职场文书
指导老师鉴定意见
2015/06/05 职场文书
Python OpenCV快速入门教程
2021/04/17 Python
MySQL 时间类型的选择
2021/06/05 MySQL
Python编写车票订购系统 Python实现快递收费系统
2022/08/14 Python