网络编程
位置:首页>> 网络编程>> Python编程>> python实现串口通信的示例代码

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

作者:雀黑够呛哥  发布时间:2023-08-04 03:44:24 

标签:python,串口通信

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还是没有自己停止,打印某个线程的堆栈。

来源:https://blog.csdn.net/qq_38875300/article/details/104094829

0
投稿

猜你喜欢

  • SQL Server数据库备份有两种方式,一种是使用BACKUP DATABASE将数据库文件备份出去,另外一种就是直接拷贝数据库文件mdf
  • 我们现在回到函数上。记得我们用 SUM 这个指令来算出所有的 Sales (营业额)吧!如果我们的需求变成是要算出每一间店 (store_n
  • 在编程时你一定碰到过时间触发的事件,在VB中有timer控件,而asp中没有, 假如你要不停地查询数据库来等待一个返回结果的话,我想你一定知
  • 背景:pony是公司的首席体验官、首席产品经理。这次在产品峰会上pony将自己平时经验的积累与大家交流,体验较细。这次分享研发管理部,设计中
  • 现在同类型的网站数不胜数,网站的功能或服务日趋同质化,大的方面看不出什么差别,差别就体现在细节上。“窥斑见豹”,细节成为网站最有力的表现形式
  • 在默认情况下,大多数浏览器都会将有序列表中的数字序列的与其列表文字内容显示为相同的字体。这篇快速教程将教你如何使用有序列表(ol)和段落(p
  • ps:不曾想还有那么好用的方法。汗一个先。Div即父容器不根据内容自适应高度,我们看下面的代码:<div id="main&
  • 如何做一个计数器并让人家申请使用?    第一步:创建一个计数器(最简单的数字计数器,不是图片式的):&nbs
  • 对设计“以人为本”和“绿色设计”两个观点的反思——兼与设计界同仁商榷Reflection of Two Views: “People-ori
  • 最近 W3C 一口气推出 7  个 HTML 工作草案,涵盖了 HTML5,HTML RDF,HTML Microdata,HTM
  • BrowserPlus 到底是什么,又能做什么?BrowserPlus 是 Yahoo! 最近刚发布一个 Web 扩展的平台:终端用户需安装
  • 前边看到有人发了个层打开效果,总感觉不是很理想 个人认为:-),如果那个层放到固定的容器里面估计就会出现问题的。今天自己来写个,可以支持 在
  • 加号+,  是字符串优先.并且从左向右计算. 就是运算前后两个值,只要有一个是字符串,就会将其中一个非字符串的试图转换成字符串.
  • 通过亲密性原则,我们可以将一个页面中的元素按照某种逻辑理解上的差异划分成不同的元素组合;再通过对齐原则,使这些不同的元素组合在视觉上看起来彼
  • 内容摘要: 当用户填写页面<FORM>内容时所提供的全部值,或在浏览器地址栏输入在URL后的值,通过Form和QueryStrin
  • 很多用ACCEE97开发过数据库的用户都有这种体会:要想在窗体中添加一个命令按钮实现打开通用对话框的功能真是很困难。因为ACCESS97本身
  • 下面代码即是VBScript代码在服务器端编译后的显示内容,如果我们把这段代码保存成静态文件(HTML)或JS文件,那么上一篇提出的问题就迎
  • 第一种情况:有RAID,还需要做数据库备份吗?回答:需要。有了RAID,万一部份磁盘损坏,可以修复数据库,有的情况下数据库甚至可以继续使用。
  • 除了第一年外,谷歌每年母亲节都会更换主页的logo以向全世界的母亲致敬。虽然2000年和2001年母亲节的logo图片看起来没什么不同,但是
  • 1. 前言但是对于很多人来说,首先编写一款 App 需要一定的移动端开发经验,其次还需要另外编写无障碍服务应用,如此显得有一定难度的本篇文章
手机版 网络编程 asp之家 www.aspxhome.com